From fd3b9610f891f166549643518e3651b0ed4467e3 Mon Sep 17 00:00:00 2001 From: Adel Haj Hassan <41540817+adel121@users.noreply.github.com> Date: Fri, 24 Nov 2023 10:55:00 +0100 Subject: [PATCH 01/87] Set AgentType to ClusterAgent when in DCA workloademta dependencies (#21079) --- cmd/cluster-agent/subcommands/start/command.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/cluster-agent/subcommands/start/command.go b/cmd/cluster-agent/subcommands/start/command.go index f588d27e8cba7..2284f94a0f2fd 100644 --- a/cmd/cluster-agent/subcommands/start/command.go +++ b/cmd/cluster-agent/subcommands/start/command.go @@ -102,6 +102,7 @@ func Commands(globalParams *command.GlobalParams) []*cobra.Command { collectors.GetCatalog(), fx.Supply(workloadmeta.Params{ InitHelper: common.GetWorkloadmetaInit(), + AgentType: workloadmeta.ClusterAgent, }), // TODO(components): check what this must be for cluster-agent-cloudfoundry fx.Supply(context.Background()), workloadmeta.Module, From b5341d16f091d9a4c74b23101617fccf911707d1 Mon Sep 17 00:00:00 2001 From: Pierre Gimalac Date: Fri, 24 Nov 2023 12:09:33 +0100 Subject: [PATCH 02/87] Update test-infra-definitions and remove waiting for fakeintake (#20959) * update test-infra-definitions and remove waiting for fakeintake * go mod tidy * update to new versio of test-infra * go mod tidy * fix nss test logs --- .gitlab-ci.yml | 4 ++-- test/new-e2e/go.mod | 10 ++++---- test/new-e2e/go.sum | 20 ++++++++-------- .../forwarder/nss_failover_test.go | 23 ++++--------------- .../flare/flare_common_test.go | 10 ++------ 5 files changed, 24 insertions(+), 43 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e08bd2a21d616..de6c6ae8e0ef9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -176,7 +176,7 @@ variables: # To use images from test-infra-definitions dev branches, set the SUFFIX variable to -dev # and check the job creating the image to make sure you have the right SHA prefix TEST_INFRA_DEFINITIONS_BUILDIMAGES_SUFFIX: "" - TEST_INFRA_DEFINITIONS_BUILDIMAGES: 2edaabddf28b + TEST_INFRA_DEFINITIONS_BUILDIMAGES: c8eaaee94472 DATADOG_AGENT_BUILDERS: v22276738-b36b132 DATADOG_AGENT_EMBEDDED_PATH: /opt/datadog-agent/embedded @@ -1001,4 +1001,4 @@ workflow: .except_mergequeue: - <<: *if_mergequeue - when: never \ No newline at end of file + when: never diff --git a/test/new-e2e/go.mod b/test/new-e2e/go.mod index 4e7fa0db8059c..6412050056886 100644 --- a/test/new-e2e/go.mod +++ b/test/new-e2e/go.mod @@ -22,7 +22,7 @@ require ( // `TEST_INFRA_DEFINITIONS_BUILDIMAGES` matches the commit sha in the module version // Example: github.com/DataDog/test-infra-definitions v0.0.0-YYYYMMDDHHmmSS-0123456789AB // => TEST_INFRA_DEFINITIONS_BUILDIMAGES: 0123456789AB - github.com/DataDog/test-infra-definitions v0.0.0-20231122132756-2edaabddf28b + github.com/DataDog/test-infra-definitions v0.0.0-20231123150722-c8eaaee94472 github.com/aws/aws-sdk-go-v2 v1.22.1 github.com/aws/aws-sdk-go-v2/config v1.18.40 github.com/aws/aws-sdk-go-v2/service/ec2 v1.130.0 @@ -35,7 +35,7 @@ require ( github.com/google/uuid v1.3.1 github.com/kr/pretty v0.3.1 github.com/pkg/sftp v1.13.6 - github.com/pulumi/pulumi/sdk/v3 v3.83.0 + github.com/pulumi/pulumi/sdk/v3 v3.84.0 github.com/samber/lo v1.38.1 github.com/sethvargo/go-retry v0.2.4 github.com/stretchr/testify v1.8.5-0.20231013065317-89920137cdfa @@ -162,8 +162,8 @@ require ( github.com/pkg/term v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pulumi/pulumi-aws/sdk/v5 v5.42.0 // indirect - github.com/pulumi/pulumi-awsx/sdk v1.0.5 // indirect - github.com/pulumi/pulumi-command/sdk v0.9.0 // indirect + github.com/pulumi/pulumi-awsx/sdk v1.0.6 // indirect + github.com/pulumi/pulumi-command/sdk v0.9.2 // indirect github.com/pulumi/pulumi-docker/sdk/v3 v3.6.1 // indirect github.com/pulumi/pulumi-eks/sdk v1.0.2 // indirect github.com/pulumi/pulumi-kubernetes/sdk/v3 v3.30.2 // indirect @@ -203,7 +203,7 @@ require ( golang.org/x/tools v0.11.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d // indirect - google.golang.org/grpc v1.57.0 // indirect + google.golang.org/grpc v1.57.1 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/test/new-e2e/go.sum b/test/new-e2e/go.sum index aae5c8269c8fa..3f08ae954c697 100644 --- a/test/new-e2e/go.sum +++ b/test/new-e2e/go.sum @@ -12,8 +12,8 @@ github.com/DataDog/datadog-api-client-go/v2 v2.15.0 h1:5UVON1xs6Lul4d6R5TmLDqqSJ github.com/DataDog/datadog-api-client-go/v2 v2.15.0/go.mod h1:ZG8wS+y2rUmkRDJZQq7Og7EAPFPage+7vXcmuah2I9o= github.com/DataDog/mmh3 v0.0.0-20200805151601-30884ca2197a h1:m9REhmyaWD5YJ0P53ygRHxKKo+KM+nw+zz0hEdKztMo= github.com/DataDog/mmh3 v0.0.0-20200805151601-30884ca2197a/go.mod h1:SvsjzyJlSg0rKsqYgdcFxeEVflx3ZNAyFfkUHP0TxXg= -github.com/DataDog/test-infra-definitions v0.0.0-20231122132756-2edaabddf28b h1:QadnsziPvqo8SIX6jfDKcLKZM64WuDPD87/uwThSgeE= -github.com/DataDog/test-infra-definitions v0.0.0-20231122132756-2edaabddf28b/go.mod h1:eyJ09i8Y8Ofu1gsfNJzL6VtIbLY0MR1atqxsculv4Lo= +github.com/DataDog/test-infra-definitions v0.0.0-20231123150722-c8eaaee94472 h1:Vl2osi/6xOlZGjtQ9tD1Jw6705vd3IDkehXr/EBmRPM= +github.com/DataDog/test-infra-definitions v0.0.0-20231123150722-c8eaaee94472/go.mod h1:PAsIkmkyjq2GD12gwu8gmjl6AdRH3IT/VMy3ycSOiG8= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/DataDog/zstd_0 v0.0.0-20210310093942-586c1286621f h1:5Vuo4niPKFkfwW55jV4vY0ih3VQ9RaQqeqY67fvRn8A= @@ -437,11 +437,11 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/pulumi/pulumi-aws/sdk/v5 v5.31.0/go.mod h1:axXtUAYEclH+SVqr/QmWFzMfJchxrrPiyMrywCcMF9A= github.com/pulumi/pulumi-aws/sdk/v5 v5.42.0 h1:QdJvPoUklXdNL8faCOuCrv7qmMNp68jiewbGH8ZboUU= github.com/pulumi/pulumi-aws/sdk/v5 v5.42.0/go.mod h1:qFeKTFSNIlMHotu9ntOWFjJBHtCiUhJeaiUB/0nVwXk= -github.com/pulumi/pulumi-awsx/sdk v1.0.5 h1:9iGZYh1tkZb7Op2TpuYiAY4Tflt4U9332K2QvuZj4G4= -github.com/pulumi/pulumi-awsx/sdk v1.0.5/go.mod h1:3oPWzeHq0zMg70zF4B/4pkQsR5QEF08+Td63hfhwCok= +github.com/pulumi/pulumi-awsx/sdk v1.0.6 h1:oUan8VgA/pqEmbS2vXhh5Zbn7Lhs6yX5bPMzM03QuMI= +github.com/pulumi/pulumi-awsx/sdk v1.0.6/go.mod h1:2H8uPHxZbfsIg9qr6yAfiIuvNnhBUqyhxw/8mXNLDFg= github.com/pulumi/pulumi-azure-native-sdk v1.104.0 h1:vyD4PvKSOkwL1z9WTis3ZE9XC73UM/7AyMNek4Vm1+E= -github.com/pulumi/pulumi-command/sdk v0.9.0 h1:YD2MMFO6caMiaBc+DIXwcGXsvuxsU2f2n3zLapltslE= -github.com/pulumi/pulumi-command/sdk v0.9.0/go.mod h1:jZaG2y3ZXEtsFKarNVva7jhAWCiaDgZMbKPfcetlA8o= +github.com/pulumi/pulumi-command/sdk v0.9.2 h1:2siCFR8pS2sSwXkeWiLrprGEtBL54FsHTzdyl125UuI= +github.com/pulumi/pulumi-command/sdk v0.9.2/go.mod h1:VeUXTI/iTgKVjRChRJbLRlBVGxAH+uymscfwzBC2VqY= github.com/pulumi/pulumi-docker/sdk/v3 v3.6.1 h1:plWLn9O6u80Vr37LoCsckyobBfcrdTU9cERor72QjqA= github.com/pulumi/pulumi-docker/sdk/v3 v3.6.1/go.mod h1:N4Yu4c49QErfucPt9Y/fGmpTryRqc0VfhyKHsGR9/g8= github.com/pulumi/pulumi-eks/sdk v1.0.2 h1:Kg+AwcuME7adnFiIDZogdAf//vIGDmhGP6PHL7lddEc= @@ -455,8 +455,8 @@ github.com/pulumi/pulumi-random/sdk/v4 v4.13.4 h1:g3jdktE5L5IDrOw4OiB+yhgxSw0okR github.com/pulumi/pulumi-random/sdk/v4 v4.13.4/go.mod h1:cFlJw0eQnqN+62QpITEF9M08gVyzNCeXrKRsuJptFak= github.com/pulumi/pulumi/sdk/v3 v3.16.0/go.mod h1:252ou/zAU1g6E8iTwe2Y9ht7pb5BDl2fJlOuAgZCHiA= github.com/pulumi/pulumi/sdk/v3 v3.50.1/go.mod h1:tqQ4z9ocyM/UI2VQ7ZReWR3w6dF5ffEozoHipOMcDh4= -github.com/pulumi/pulumi/sdk/v3 v3.83.0 h1:Qjek0K/GaHYAMahMR5m5v/JWQuWg6eG0M/7swy9Ls8U= -github.com/pulumi/pulumi/sdk/v3 v3.83.0/go.mod h1:RMilNNVMlmK1h4Nl/qylb9vzbgh4F3mufZoUOnPy98o= +github.com/pulumi/pulumi/sdk/v3 v3.84.0 h1:/vCRj6ATGVZw4pFmG7pZgjlKUcnbbnb9vmlqd+OpdXo= +github.com/pulumi/pulumi/sdk/v3 v3.84.0/go.mod h1:RMilNNVMlmK1h4Nl/qylb9vzbgh4F3mufZoUOnPy98o= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= @@ -737,8 +737,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= +google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= 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/test/new-e2e/tests/agent-shared-components/forwarder/nss_failover_test.go b/test/new-e2e/tests/agent-shared-components/forwarder/nss_failover_test.go index 3664c914be38b..e25bc212bf7ff 100644 --- a/test/new-e2e/tests/agent-shared-components/forwarder/nss_failover_test.go +++ b/test/new-e2e/tests/agent-shared-components/forwarder/nss_failover_test.go @@ -31,6 +31,7 @@ import ( fi "github.com/DataDog/datadog-agent/test/fakeintake/client" "github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e" "github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e/client" + "github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e/params" ) type multiFakeIntakeEnv struct { @@ -109,21 +110,7 @@ type multiFakeIntakeSuite struct { } func TestMultiFakeintakeSuite(t *testing.T) { - e2e.Run(t, &multiFakeIntakeSuite{}, multiFakeintakeStackDef()) -} - -// SetupSuite waits for both fakeintakes to be ready before running tests. -func (v *multiFakeIntakeSuite) SetupSuite() { - v.Suite.SetupSuite() // need to call the original definition of SetupSuite - - fakeintake1 := v.Env().Fakeintake1 - fakeintake2 := v.Env().Fakeintake2 - - // Wait for the fakeintakes to be ready to avoid 503 - require.EventuallyWithT(v.T(), func(c *assert.CollectT) { - assert.NoError(c, fakeintake1.GetServerHealth()) - assert.NoError(c, fakeintake2.GetServerHealth()) - }, intakeMaxWaitTime, intakeTick) + e2e.Run(t, &multiFakeIntakeSuite{}, multiFakeintakeStackDef(), params.WithStackName("mystack000")) } // TestNSSFailover tests that the agent correctly picks-up an NSS change of the intake. @@ -165,7 +152,6 @@ func (v *multiFakeIntakeSuite) TestNSSFailover() { v.UpdateEnv(multiFakeintakeStackDef(agentOptions...)) // check that fakeintake1 is used as intake and not fakeintake2 - v.T().Logf("checking that the agent contacts main intake at %s", fakeintake1IP) v.requireIntakeIsUsed(v.Env().Fakeintake1, intakeMaxWaitTime, intakeTick) v.requireIntakeNotUsed(v.Env().Fakeintake2, intakeMaxWaitTime, intakeTick) @@ -175,7 +161,6 @@ func (v *multiFakeIntakeSuite) TestNSSFailover() { setHostEntry(v.T(), v.Env().VM, intakeName, fakeintake2IP) // check that fakeintake2 is used as intake and not fakeintake1 - v.T().Logf("checking that the agent contacts fallback intake at %s", fakeintake2IP) intakeMaxWaitTime := connectionResetInterval*time.Second + intakeMaxWaitTime v.requireIntakeIsUsed(v.Env().Fakeintake2, intakeMaxWaitTime, intakeTick) v.requireIntakeNotUsed(v.Env().Fakeintake1, intakeMaxWaitTime, intakeTick) @@ -204,6 +189,7 @@ func (v *multiFakeIntakeSuite) requireIntakeIsUsed(intake *client.Fakeintake, in assert.NoError(t, err) } + v.T().Logf("checking that the agent contacts intake at %s", intake.URL()) require.EventuallyWithT(v.T(), checkFn, intakeMaxWaitTime, intakeTick) } @@ -228,7 +214,8 @@ func (v *multiFakeIntakeSuite) requireIntakeNotUsed(intake *client.Fakeintake, i assert.Empty(t, stats) } - require.EventuallyWithT(v.T(), checkFn, intakeMaxWaitTime, intakeTick+intakeUnusedWaitTime) + v.T().Logf("checking that the agent doesn't contact intake at %s", intake.URL()) + require.EventuallyWithT(v.T(), checkFn, intakeMaxWaitTime, intakeTick) } // setHostEntry adds an entry in /etc/hosts for the given hostname and hostIP diff --git a/test/new-e2e/tests/agent-subcommands/flare/flare_common_test.go b/test/new-e2e/tests/agent-subcommands/flare/flare_common_test.go index 0d77396c32cd0..473697c806dd5 100644 --- a/test/new-e2e/tests/agent-subcommands/flare/flare_common_test.go +++ b/test/new-e2e/tests/agent-subcommands/flare/flare_common_test.go @@ -9,13 +9,12 @@ package flare import ( _ "embed" "testing" - "time" + + "github.com/stretchr/testify/require" "github.com/DataDog/datadog-agent/test/fakeintake/client/flare" "github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e" "github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e/client" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) type baseFlareSuite struct { @@ -39,11 +38,6 @@ func (v *baseFlareSuite) TestFlareDefaultFiles() { } func requestAgentFlareAndFetchFromFakeIntake(t *testing.T, agent client.Agent, fakeintake *client.Fakeintake, flareArgs ...client.AgentArgsOption) flare.Flare { - // Wait for the fakeintake to be ready to avoid 503 when sending the flare - assert.EventuallyWithT(t, func(c *assert.CollectT) { - assert.NoError(c, fakeintake.Client.GetServerHealth()) - }, 5*time.Minute, 20*time.Second) - _ = agent.Flare(flareArgs...) flare, err := fakeintake.Client.GetLatestFlare() From d296ae4354e4ee93c479514fed0c9d7f9d223b4e Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Fri, 24 Nov 2023 12:58:30 +0100 Subject: [PATCH 03/87] [ebpf] fix the `getEmbeddedBTF` platform selection algorithm (#21077) * add a new test to `TestEmbeddedBTFMatch` The goal here is to have a test that go through the strong association based on kernel version patterns code path it hads the following hierarchy to `minimized-btfs.tar.xz`: centos |- centos/3.10.0-1062.0.0.0.1.el7.x86_64.btf.tar.xz fakecentos |- centos/3.10.0-1062.0.0.0.1.el7.x86_64.btf.tar.xz The algorithm should remove the fakecentos option * fix `slices.DeleteFunc` call --- pkg/ebpf/btf.go | 2 +- pkg/ebpf/btf_test.go | 2 ++ pkg/ebpf/testdata/minimized-btfs.tar.xz | Bin 4103828 -> 4106552 bytes 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/ebpf/btf.go b/pkg/ebpf/btf.go index a64c73a1c9cc1..445ec9b5a9364 100644 --- a/pkg/ebpf/btf.go +++ b/pkg/ebpf/btf.go @@ -240,7 +240,7 @@ func (b *orderedBTFLoader) getEmbeddedBTF(platform, platformVersion, kernelVersi for _, kvp := range kernelVersionPatterns { if kvp.pattern.MatchString(kernelVersion) { // remove possible paths that do not match possible platforms - slices.DeleteFunc(possiblePaths, func(s string) bool { + possiblePaths = slices.DeleteFunc(possiblePaths, func(s string) bool { pform := strings.Split(s, string(os.PathSeparator))[0] return !slices.Contains(kvp.platforms, pform) }) diff --git a/pkg/ebpf/btf_test.go b/pkg/ebpf/btf_test.go index 9853dbaf6bcf9..cc593fa305270 100644 --- a/pkg/ebpf/btf_test.go +++ b/pkg/ebpf/btf_test.go @@ -40,6 +40,8 @@ func TestEmbeddedBTFMatch(t *testing.T) { {"ubuntu", "22.04", "5.4.0-80-generic", "", true}, // non-existent kernel version {"ubuntu", "22.04", "15.0", "", true}, + // kernel is in testdata for centos, and fakecentos, so multiple matches, but platform filter + {"redhat", "7", "3.10.0-1062.0.0.0.1.el7.x86_64", "centos/3.10.0-1062.0.0.0.1.el7.x86_64.btf.tar.xz", false}, } for i, test := range tests { diff --git a/pkg/ebpf/testdata/minimized-btfs.tar.xz b/pkg/ebpf/testdata/minimized-btfs.tar.xz index 9602bb999f0ba13f7dd2fddc3bbb704f2de1bb07..e9b983951044c67756157def5678044e3db9ca0f 100644 GIT binary patch delta 235638 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ⅅrw9rPYMAlzWjZKEG8gD$0A2tSOm`W#sZrH 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
    *ew;GPb`-CctLYKSnfMxz=c+xs|;q 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 delta 232900 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

    =R%fxn2zam65e7 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

  1. B-d%ZohHhlZYvkpme(GQ&o2I~1-kb3Jy*i|ik; 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 From 06766e50d9ba9e16d91895b735bdf856bc8ea9b4 Mon Sep 17 00:00:00 2001 From: Dario Meloni Date: Fri, 24 Nov 2023 13:09:07 +0100 Subject: [PATCH 04/87] Trace-agent doesn't forward 404s to tracers (#21016) * Trace-agent forward 404s to tracers When remote configuration is in a failed state the trace agent always returned 500s * Simplify code that can't file * Added release note * Removed log message * Update releasenotes/notes/fix-tracer-agent-forwards-remote-config-errors-1e939be195e6db67.yaml Co-authored-by: Ahmed Mezghani <38987709+ahmed-mez@users.noreply.github.com> * Added Unit Test * Close body * Remove returned error handling --------- Co-authored-by: Ahmed Mezghani <38987709+ahmed-mez@users.noreply.github.com> --- cmd/agent/subcommands/run/command.go | 4 +-- .../subcommands/start/command.go | 4 +-- cmd/trace-agent/config/remote/config.go | 11 ++++++- cmd/trace-agent/config/remote/config_test.go | 29 ++++++++++++++++++- pkg/config/remote/service/service.go | 3 +- ...remote-config-errors-1e939be195e6db67.yaml | 5 ++++ 6 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 releasenotes/notes/fix-tracer-agent-forwards-remote-config-errors-1e939be195e6db67.yaml diff --git a/cmd/agent/subcommands/run/command.go b/cmd/agent/subcommands/run/command.go index b679a7fbd2d7a..54650cee453a3 100644 --- a/cmd/agent/subcommands/run/command.go +++ b/cmd/agent/subcommands/run/command.go @@ -481,8 +481,8 @@ func startAgent( configService, err = remoteconfig.NewService() if err != nil { log.Errorf("Failed to initialize config management service: %s", err) - } else if err := configService.Start(context.Background()); err != nil { - log.Errorf("Failed to start config management service: %s", err) + } else { + configService.Start(context.Background()) } if err := rcclient.Start("core-agent"); err != nil { diff --git a/cmd/cluster-agent/subcommands/start/command.go b/cmd/cluster-agent/subcommands/start/command.go index 2284f94a0f2fd..85dfb0523e0a5 100644 --- a/cmd/cluster-agent/subcommands/start/command.go +++ b/cmd/cluster-agent/subcommands/start/command.go @@ -444,9 +444,7 @@ func initializeRemoteConfig(ctx context.Context) (*remote.Client, error) { return nil, fmt.Errorf("unable to create remote-config service: %w", err) } - if err := configService.Start(ctx); err != nil { - return nil, fmt.Errorf("unable to start remote-config service: %w", err) - } + configService.Start(ctx) rcClient, err := remote.NewClient("cluster-agent", configService, version.AgentVersion, []data.Product{data.ProductAPMTracing}, time.Second*5) if err != nil { diff --git a/cmd/trace-agent/config/remote/config.go b/cmd/trace-agent/config/remote/config.go index 315e879f5fcbe..9f6e61854abfc 100644 --- a/cmd/trace-agent/config/remote/config.go +++ b/cmd/trace-agent/config/remote/config.go @@ -22,6 +22,8 @@ import ( "github.com/DataDog/datadog-agent/pkg/trace/metrics/timing" "github.com/DataDog/datadog-agent/pkg/trace/traceutil" "github.com/DataDog/datadog-agent/pkg/util/log" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) var bufferPool = sync.Pool{ @@ -74,7 +76,14 @@ func ConfigHandler(r *api.HTTPReceiver, client remote.ConfigUpdater, cfg *config } cfg, err := client.ClientGetConfigs(req.Context(), &configsRequest) if err != nil { - statusCode = http.StatusInternalServerError + if e, ok := status.FromError(err); ok { + switch e.Code() { + case codes.Unimplemented, codes.NotFound: + statusCode = http.StatusNotFound + } + } else { + statusCode = http.StatusInternalServerError + } http.Error(w, err.Error(), statusCode) return } diff --git a/cmd/trace-agent/config/remote/config_test.go b/cmd/trace-agent/config/remote/config_test.go index 4e56731211994..6c605bea2989b 100644 --- a/cmd/trace-agent/config/remote/config_test.go +++ b/cmd/trace-agent/config/remote/config_test.go @@ -22,6 +22,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) func TestConfigEndpoint(t *testing.T) { @@ -153,6 +156,26 @@ func TestUpstreamRequest(t *testing.T) { } } +func TestForwardErrors(t *testing.T) { + assert := assert.New(t) + grpc := agentGRPCConfigFetcher{} + rcv := api.NewHTTPReceiver(config.New(), sampler.NewDynamicConfig(), make(chan *api.Payload, 5000), nil, telemetry.NewNoopCollector()) + + grpc.On("ClientGetConfigs", mock.Anything, mock.Anything, mock.Anything). + Return(nil, status.Error(codes.Unimplemented, "not implemented")) + + mux := http.NewServeMux() + mux.Handle("/v0.7/config", ConfigHandler(rcv, &grpc, &config.AgentConfig{})) + server := httptest.NewServer(mux) + + req, _ := http.NewRequest("POST", server.URL+"/v0.7/config", strings.NewReader(`{"client":{"id":"test_client","is_tracer":true,"client_tracer":{"service":"test","tags":["foo:bar"]}}}`)) + req.Header.Set("Datadog-Container-ID", "cid") + r, err := http.DefaultClient.Do(req) + assert.NoError(err) + assert.Equal(404, r.StatusCode) + r.Body.Close() +} + type agentGRPCConfigFetcher struct { pbgo.AgentSecureClient mock.Mock @@ -160,5 +183,9 @@ type agentGRPCConfigFetcher struct { func (a *agentGRPCConfigFetcher) ClientGetConfigs(ctx context.Context, in *pbgo.ClientGetConfigsRequest) (*pbgo.ClientGetConfigsResponse, error) { args := a.Called(ctx, in) - return args.Get(0).(*pbgo.ClientGetConfigsResponse), args.Error(1) + var maybeResponse *pbgo.ClientGetConfigsResponse + if args.Get(0) != nil { + maybeResponse = args.Get(0).(*pbgo.ClientGetConfigsResponse) + } + return maybeResponse, args.Error(1) } diff --git a/pkg/config/remote/service/service.go b/pkg/config/remote/service/service.go index a8d5b08d36c59..dfbf79feaa9b7 100644 --- a/pkg/config/remote/service/service.go +++ b/pkg/config/remote/service/service.go @@ -270,7 +270,7 @@ func newRCBackendOrgUUIDProvider(http api.API) uptane.OrgUUIDProvider { } // Start the remote configuration management service -func (s *Service) Start(ctx context.Context) error { +func (s *Service) Start(ctx context.Context) { ctx, cancel := context.WithCancel(ctx) s.cancel = cancel go func() { @@ -324,7 +324,6 @@ func (s *Service) Start(ctx context.Context) error { } } }() - return nil } func (s *Service) Stop() error { diff --git a/releasenotes/notes/fix-tracer-agent-forwards-remote-config-errors-1e939be195e6db67.yaml b/releasenotes/notes/fix-tracer-agent-forwards-remote-config-errors-1e939be195e6db67.yaml new file mode 100644 index 0000000000000..4d8884d5f042d --- /dev/null +++ b/releasenotes/notes/fix-tracer-agent-forwards-remote-config-errors-1e939be195e6db67.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + APM: Fixed trace-agent not forwarding errors from remote configuration and reporting them all as 500s + From 464000298275a4901789fb0900892efb7507a3e8 Mon Sep 17 00:00:00 2001 From: Nicolas Schweitzer Date: Fri, 24 Nov 2023 14:11:50 +0100 Subject: [PATCH 05/87] Prevent generation of new jobs in mq context (#21087) Prevent generation of new jobs in mq context --- .gitlab/source_test_stats/linux.yml | 3 +++ .gitlab/source_test_stats/windows.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.gitlab/source_test_stats/linux.yml b/.gitlab/source_test_stats/linux.yml index c7c6519d1e0f1..852c457011c14 100644 --- a/.gitlab/source_test_stats/linux.yml +++ b/.gitlab/source_test_stats/linux.yml @@ -3,6 +3,9 @@ stats-fast-tests-deb-x64-py3: image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/datadog-ci-uploader$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES tags: ["arch:amd64"] needs: ["tests_deb-x64-py3-fast", "tests_deb-x64-py3"] + rules: + - !reference [.except_mergequeue] + - when: on_success script: - set +x - export DD_API_KEY=$(aws ssm get-parameter --region us-east-1 --name ci.datadog-agent.datadog_api_key_org2 --with-decryption --query "Parameter.Value" --out text) diff --git a/.gitlab/source_test_stats/windows.yml b/.gitlab/source_test_stats/windows.yml index 8f1da30e1f23e..347adcfa8b359 100644 --- a/.gitlab/source_test_stats/windows.yml +++ b/.gitlab/source_test_stats/windows.yml @@ -3,6 +3,9 @@ stats-fast-tests-windows-x64: image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/datadog-ci-uploader$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES tags: ["arch:amd64"] needs: ["tests_windows-x64-fast", "tests_windows-x64"] + rules: + - !reference [.except_mergequeue] + - when: on_success script: - set +x - export DD_API_KEY=$(aws ssm get-parameter --region us-east-1 --name ci.datadog-agent.datadog_api_key_org2 --with-decryption --query "Parameter.Value" --out text) From 5a3f18bb32116c85cec460c99c8fb68bc829a48f Mon Sep 17 00:00:00 2001 From: Kevin Fairise <132568982+KevinFairise2@users.noreply.github.com> Date: Fri, 24 Nov 2023 15:27:49 +0100 Subject: [PATCH 06/87] Temporary remove debian 9 which is Flaky (#21085) Temporary remove debian 9 which is Flaky --- .gitlab/new-e2e_testing/debian.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/new-e2e_testing/debian.yml b/.gitlab/new-e2e_testing/debian.yml index 4989459fc00f8..a0c2702189897 100644 --- a/.gitlab/new-e2e_testing/debian.yml +++ b/.gitlab/new-e2e_testing/debian.yml @@ -5,7 +5,7 @@ .new-e2e_debian_a7_x64: variables: - E2E_OSVERS: "debian-9,debian-10,debian-11,debian-12" + E2E_OSVERS: "debian-10,debian-11,debian-12" E2E_CWS_SUPPORTED_OSVERS: "debian-10,debian-11" E2E_BRANCH_OSVERS: "debian-11" needs: ["deploy_deb_testing-a7_x64"] From 80c6ce37436f9e86a57f184ca79afc1af1b357e0 Mon Sep 17 00:00:00 2001 From: Guillaume Fournier <36961134+Gui774ume@users.noreply.github.com> Date: Fri, 24 Nov 2023 16:04:02 +0100 Subject: [PATCH 07/87] [CWS] fix auto suppression feature (#20882) --- cmd/system-probe/config/adjust_security.go | 10 ++++++++++ pkg/config/system_probe_cws.go | 2 +- pkg/security/config/config.go | 20 +++++++++---------- .../security_profile/profile/manager.go | 2 +- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/cmd/system-probe/config/adjust_security.go b/cmd/system-probe/config/adjust_security.go index b884ddeead6f4..6f4a2ed611fb0 100644 --- a/cmd/system-probe/config/adjust_security.go +++ b/cmd/system-probe/config/adjust_security.go @@ -18,6 +18,16 @@ func adjustSecurity(cfg config.Config) { return time.Duration(cfg.GetInt(secNS("activity_dump.cgroup_dump_timeout"))) * time.Minute }) + deprecateCustom( + cfg, + secNS("runtime_security_config.security_profile.anomaly_detection.auto_suppression.enabled"), + secNS("runtime_security_config.security_profile.auto_suppression.enabled"), + func(cfg config.Config) interface{} { + // convert old auto suppression parameter to the new one + return cfg.GetBool(secNS("runtime_security_config.security_profile.anomaly_detection.auto_suppression.enabled")) + }, + ) + if cfg.GetBool(secNS("enabled")) { // if runtime is enabled then we force fim cfg.Set(secNS("fim_enabled"), true, model.SourceAgentRuntime) diff --git a/pkg/config/system_probe_cws.go b/pkg/config/system_probe_cws.go index fb89071e50d07..9ab30cdd3439a 100644 --- a/pkg/config/system_probe_cws.go +++ b/pkg/config/system_probe_cws.go @@ -55,7 +55,6 @@ func initCWSSystemProbeConfig(cfg Config) { cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.tag_rules.enabled", true) cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.silent_workloads.delay", "10s") cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.silent_workloads.ticker", "10s") - cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.auto_suppression.enabled", false) cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.workload_deny_list", []string{}) // CWS - SBOM @@ -84,6 +83,7 @@ func initCWSSystemProbeConfig(cfg Config) { cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.rate_limiter.num_events_allowed", 100) cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.tag_rules.enabled", true) cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.silent_rule_events.enabled", false) + cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.auto_suppression.enabled", false) // CWS - Hash algorithms cfg.BindEnvAndSetDefault("runtime_security_config.hash_resolver.enabled", true) diff --git a/pkg/security/config/config.go b/pkg/security/config/config.go index f6a10cecea925..5ecdd2f3cabf6 100644 --- a/pkg/security/config/config.go +++ b/pkg/security/config/config.go @@ -127,8 +127,6 @@ type RuntimeSecurityConfig struct { ActivityDumpSilentWorkloadsDelay time.Duration // ActivityDumpSilentWorkloadsTicker configures ticker that will check if a workload is silent and should be traced ActivityDumpSilentWorkloadsTicker time.Duration - // ActivityDumpAutoSuppressionEnabled do not send event if part of a profile - ActivityDumpAutoSuppressionEnabled bool // # Dynamic configuration fields: // ActivityDumpMaxDumpSize defines the maximum size of a dump @@ -148,6 +146,8 @@ type RuntimeSecurityConfig struct { SecurityProfileRCEnabled bool // SecurityProfileDNSMatchMaxDepth defines the max depth of subdomain to be matched for DNS anomaly detection (0 to match everything) SecurityProfileDNSMatchMaxDepth int + // SecurityProfileAutoSuppressionEnabled do not send event if part of a profile + SecurityProfileAutoSuppressionEnabled bool // AnomalyDetectionEventTypes defines the list of events that should be allowed to generate anomaly detections AnomalyDetectionEventTypes []model.EventType @@ -282,7 +282,6 @@ func NewRuntimeSecurityConfig() (*RuntimeSecurityConfig, error) { ActivityDumpSilentWorkloadsDelay: coreconfig.SystemProbe.GetDuration("runtime_security_config.activity_dump.silent_workloads.delay"), ActivityDumpSilentWorkloadsTicker: coreconfig.SystemProbe.GetDuration("runtime_security_config.activity_dump.silent_workloads.ticker"), ActivityDumpWorkloadDenyList: coreconfig.SystemProbe.GetStringSlice("runtime_security_config.activity_dump.workload_deny_list"), - ActivityDumpAutoSuppressionEnabled: coreconfig.SystemProbe.GetBool("runtime_security_config.security_profile.anomaly_detection.auto_suppression.enabled"), // activity dump dynamic fields ActivityDumpMaxDumpSize: func() int { mds := coreconfig.SystemProbe.GetInt("runtime_security_config.activity_dump.max_dump_size") @@ -306,13 +305,14 @@ func NewRuntimeSecurityConfig() (*RuntimeSecurityConfig, error) { HashResolverCacheSize: coreconfig.SystemProbe.GetInt("runtime_security_config.hash_resolver.cache_size"), // security profiles - SecurityProfileEnabled: coreconfig.SystemProbe.GetBool("runtime_security_config.security_profile.enabled"), - SecurityProfileDir: coreconfig.SystemProbe.GetString("runtime_security_config.security_profile.dir"), - SecurityProfileWatchDir: coreconfig.SystemProbe.GetBool("runtime_security_config.security_profile.watch_dir"), - SecurityProfileCacheSize: coreconfig.SystemProbe.GetInt("runtime_security_config.security_profile.cache_size"), - SecurityProfileMaxCount: coreconfig.SystemProbe.GetInt("runtime_security_config.security_profile.max_count"), - SecurityProfileRCEnabled: coreconfig.SystemProbe.GetBool("runtime_security_config.security_profile.remote_configuration.enabled"), - SecurityProfileDNSMatchMaxDepth: coreconfig.SystemProbe.GetInt("runtime_security_config.security_profile.dns_match_max_depth"), + SecurityProfileEnabled: coreconfig.SystemProbe.GetBool("runtime_security_config.security_profile.enabled"), + SecurityProfileDir: coreconfig.SystemProbe.GetString("runtime_security_config.security_profile.dir"), + SecurityProfileWatchDir: coreconfig.SystemProbe.GetBool("runtime_security_config.security_profile.watch_dir"), + SecurityProfileCacheSize: coreconfig.SystemProbe.GetInt("runtime_security_config.security_profile.cache_size"), + SecurityProfileMaxCount: coreconfig.SystemProbe.GetInt("runtime_security_config.security_profile.max_count"), + SecurityProfileRCEnabled: coreconfig.SystemProbe.GetBool("runtime_security_config.security_profile.remote_configuration.enabled"), + SecurityProfileDNSMatchMaxDepth: coreconfig.SystemProbe.GetInt("runtime_security_config.security_profile.dns_match_max_depth"), + SecurityProfileAutoSuppressionEnabled: coreconfig.SystemProbe.GetBool("runtime_security_config.security_profile.auto_suppression.enabled"), // anomaly detection AnomalyDetectionEventTypes: parseEventTypeStringSlice(coreconfig.SystemProbe.GetStringSlice("runtime_security_config.security_profile.anomaly_detection.event_types")), diff --git a/pkg/security/security_profile/profile/manager.go b/pkg/security/security_profile/profile/manager.go index 6437d2ff3134d..9cb5517a53faa 100644 --- a/pkg/security/security_profile/profile/manager.go +++ b/pkg/security/security_profile/profile/manager.go @@ -178,7 +178,7 @@ func NewSecurityProfileManager(config *config.Config, statsdClient statsd.Client if len(config.RuntimeSecurity.SecurityProfileDir) != 0 { // override the status if autosuppression is enabled var status model.Status - if config.RuntimeSecurity.ActivityDumpAutoSuppressionEnabled { + if config.RuntimeSecurity.SecurityProfileAutoSuppressionEnabled { status = model.AnomalyDetection | model.AutoSuppression } From a4dda346c4dcf0e901c348c3103f060fcdd66bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Beauz=C3=A9e-Luyssen?= Date: Fri, 24 Nov 2023 16:39:30 +0100 Subject: [PATCH 08/87] CI: don't build suse arm64 by default (#21080) CI: don't build suse arm64 by default --- .gitlab/package_build/suse_rpm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/package_build/suse_rpm.yml b/.gitlab/package_build/suse_rpm.yml index 3baf7176a322f..6e295ef1f76e4 100644 --- a/.gitlab/package_build/suse_rpm.yml +++ b/.gitlab/package_build/suse_rpm.yml @@ -74,7 +74,7 @@ agent_suse-x64-a7: agent_suse-arm64-a7: extends: .agent_build_common_suse_rpm rules: - !reference [.on_a7] + !reference [.on_all_builds_a7] stage: package_build image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES tags: ["arch:arm64"] From e6bef49d6aa89ef6201803afecfaa8446b882676 Mon Sep 17 00:00:00 2001 From: Guillaume Fournier <36961134+Gui774ume@users.noreply.github.com> Date: Fri, 24 Nov 2023 17:13:34 +0100 Subject: [PATCH 09/87] [CWS] add flag to disable anomaly detection (#21070) --- pkg/config/system_probe_cws.go | 21 +++++++++++---------- pkg/security/config/config.go | 2 ++ pkg/security/probe/probe_linux.go | 4 +++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/pkg/config/system_probe_cws.go b/pkg/config/system_probe_cws.go index 9ab30cdd3439a..e3a91df1c60ff 100644 --- a/pkg/config/system_probe_cws.go +++ b/pkg/config/system_probe_cws.go @@ -39,10 +39,10 @@ func initCWSSystemProbeConfig(cfg Config) { cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.load_controller_period", "60s") cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.min_timeout", "10m") cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.max_dump_size", 1750) - cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.traced_cgroups_count", 5) + cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.traced_cgroups_count", 10) cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.traced_event_types", []string{"exec", "open", "dns"}) cfg.BindEnv("runtime_security_config.activity_dump.cgroup_dump_timeout") // deprecated in favor of dump_duration - cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.dump_duration", "1800s") + cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.dump_duration", "900s") cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.rate_limiter", 500) cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.cgroup_wait_list_timeout", "4500s") cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.cgroup_differentiate_args", false) @@ -69,21 +69,22 @@ func initCWSSystemProbeConfig(cfg Config) { cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.max_count", 400) cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.remote_configuration.enabled", false) cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.dns_match_max_depth", 3) + cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.auto_suppression.enabled", true) // CWS - Anomaly detection - cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.event_types", []string{"exec", "dns"}) - cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.default_minimum_stable_period", "48h") - cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.minimum_stable_period.exec", "48h") - cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.minimum_stable_period.dns", "96h") + cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.event_types", []string{"exec", "dns", "open"}) + cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.default_minimum_stable_period", "900s") + cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.minimum_stable_period.exec", "900s") + cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.minimum_stable_period.dns", "900s") cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.workload_warmup_period", "180s") cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.unstable_profile_time_threshold", "120h") cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.unstable_profile_size_threshold", 5000000) - cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.rate_limiter.period", "1s") - cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.rate_limiter.num_keys", 400) - cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.rate_limiter.num_events_allowed", 100) + cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.rate_limiter.period", "5m") + cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.rate_limiter.num_keys", 1000) + cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.rate_limiter.num_events_allowed", 20) cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.tag_rules.enabled", true) cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.silent_rule_events.enabled", false) - cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.auto_suppression.enabled", false) + cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.enabled", false) // CWS - Hash algorithms cfg.BindEnvAndSetDefault("runtime_security_config.hash_resolver.enabled", true) diff --git a/pkg/security/config/config.go b/pkg/security/config/config.go index 5ecdd2f3cabf6..a6a3d0b0d6d28 100644 --- a/pkg/security/config/config.go +++ b/pkg/security/config/config.go @@ -179,6 +179,8 @@ type RuntimeSecurityConfig struct { AnomalyDetectionTagRulesEnabled bool // AnomalyDetectionSilentRuleEventsEnabled do not send rule event if also part of an anomaly event AnomalyDetectionSilentRuleEventsEnabled bool + // AnomalyDetectionEnabled defines if we should send anomaly detection events + AnomalyDetectionEnabled bool // SBOMResolverEnabled defines if the SBOM resolver should be enabled SBOMResolverEnabled bool diff --git a/pkg/security/probe/probe_linux.go b/pkg/security/probe/probe_linux.go index 6654e970cc365..eb2c6c1678a42 100644 --- a/pkg/security/probe/probe_linux.go +++ b/pkg/security/probe/probe_linux.go @@ -395,7 +395,9 @@ func (p *Probe) DispatchEvent(event *model.Event) { if event.IsKernelSpaceAnomalyDetectionEvent() { p.profileManagers.securityProfileManager.FillProfileContextFromContainerID(event.FieldHandlers.ResolveContainerID(event, event.ContainerContext), &event.SecurityProfileContext) } - p.sendAnomalyDetection(event) + if p.Config.RuntimeSecurity.AnomalyDetectionEnabled { + p.sendAnomalyDetection(event) + } } else if event.Error == nil { // Process event after evaluation because some monitors need the DentryResolver to have been called first. if p.profileManagers.activityDumpManager != nil { From 1b5a2ef67f3a2fd5e62f1aebda0e2fd03734c60d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Beauz=C3=A9e-Luyssen?= Date: Fri, 24 Nov 2023 18:57:58 +0100 Subject: [PATCH 10/87] macOS: split linting & testing (#20888) macOS: split linting & testing --- .gitlab/source_test/macos.yml | 19 +++++++++++++++++++ tasks/github_tasks.py | 22 ++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/.gitlab/source_test/macos.yml b/.gitlab/source_test/macos.yml index d20acf5ffb6f0..efe673b5da86e 100644 --- a/.gitlab/source_test/macos.yml +++ b/.gitlab/source_test/macos.yml @@ -27,3 +27,22 @@ tests_macos: - junit-*-repacked.tgz reports: junit: "**/junit-out-*.xml" + +lint_macos: + stage: source_test + rules: + !reference [.on_a6] + image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES + tags: ["arch:amd64"] + variables: + PYTHON_RUNTIMES: '3' + script: + - source /root/.bashrc + - export GITHUB_KEY_B64=$(aws ssm get-parameter --region us-east-1 --name ci.datadog-agent.macos_github_key_b64 --with-decryption --query "Parameter.Value" --out text) + - export GITHUB_APP_ID=$(aws ssm get-parameter --region us-east-1 --name ci.datadog-agent.macos_github_app_id --with-decryption --query "Parameter.Value" --out text) + - export GITHUB_INSTALLATION_ID=$(aws ssm get-parameter --region us-east-1 --name ci.datadog-agent.macos_github_installation_id --with-decryption --query "Parameter.Value" --out text) + - $S3_CP_CMD $S3_ARTIFACTS_URI/agent-version.cache . + - export VERSION_CACHE_CONTENT=$(cat agent-version.cache | base64 -) + - !reference [.setup_python_mirror_linux] + - python3 -m pip install -r tasks/libs/requirements-github.txt + - inv -e github.trigger-macos-lint --datadog-agent-ref "$CI_COMMIT_SHA" --python-runtimes "$PYTHON_RUNTIMES" --version-cache "$VERSION_CACHE_CONTENT" diff --git a/tasks/github_tasks.py b/tasks/github_tasks.py index 26832abda6927..e2a7815ebbfff 100644 --- a/tasks/github_tasks.py +++ b/tasks/github_tasks.py @@ -14,7 +14,7 @@ from .utils import DEFAULT_BRANCH -def _trigger_macos_workflow(release, destination, retry_download, retry_interval, **kwargs): +def _trigger_macos_workflow(release, destination=None, retry_download=0, retry_interval=0, **kwargs): github_action_ref = _get_release_json_value(f'{release}::MACOS_BUILD_VERSION') run = trigger_macos_workflow( @@ -29,7 +29,8 @@ def _trigger_macos_workflow(release, destination, retry_download, retry_interval print_workflow_conclusion(workflow_conclusion, workflow_url) - download_with_retry(download_artifacts, run, destination, retry_download, retry_interval) + if destination: + download_with_retry(download_artifacts, run, destination, retry_download, retry_interval) if workflow_conclusion != "success": raise Exit(code=1) @@ -91,6 +92,23 @@ def trigger_macos_test( ) +@task +def trigger_macos_lint( + _, + datadog_agent_ref=DEFAULT_BRANCH, + release_version="nightly-a7", + python_runtimes="3", + version_cache=None, +): + _trigger_macos_workflow( + release_version, + workflow_name="lint.yaml", + datadog_agent_ref=datadog_agent_ref, + python_runtimes=python_runtimes, + version_cache_file_content=version_cache, + ) + + @task def lint_codeowner(_): """ From d64f748b7aa1349023c3a7f9f0847b89838da3c6 Mon Sep 17 00:00:00 2001 From: Kevin Fairise <132568982+KevinFairise2@users.noreply.github.com> Date: Fri, 24 Nov 2023 20:03:49 +0100 Subject: [PATCH 11/87] Welcome back debian 9 (#21092) Welcome back debian 9 --- .gitlab/new-e2e_testing/debian.yml | 2 +- test/new-e2e/tests/agent-platform/platforms/platforms.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/new-e2e_testing/debian.yml b/.gitlab/new-e2e_testing/debian.yml index a0c2702189897..4989459fc00f8 100644 --- a/.gitlab/new-e2e_testing/debian.yml +++ b/.gitlab/new-e2e_testing/debian.yml @@ -5,7 +5,7 @@ .new-e2e_debian_a7_x64: variables: - E2E_OSVERS: "debian-10,debian-11,debian-12" + E2E_OSVERS: "debian-9,debian-10,debian-11,debian-12" E2E_CWS_SUPPORTED_OSVERS: "debian-10,debian-11" E2E_BRANCH_OSVERS: "debian-11" needs: ["deploy_deb_testing-a7_x64"] diff --git a/test/new-e2e/tests/agent-platform/platforms/platforms.json b/test/new-e2e/tests/agent-platform/platforms/platforms.json index f76961cd49a50..655c8b767a1c4 100644 --- a/test/new-e2e/tests/agent-platform/platforms/platforms.json +++ b/test/new-e2e/tests/agent-platform/platforms/platforms.json @@ -1,7 +1,7 @@ { "debian": { "x86_64": { - "debian-9": "ami-0272fcfb8ca47d813", + "debian-9": "ami-06441ffb13c981873", "debian-10": "ami-041540a5c191757a0", "debian-11": "ami-09e24b0cfe072ecef", "debian-12": "ami-06db4d78cb1d3bbf9" From 8805c7aa068ddb7220f498f621b9094f5ac4a866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9na=C3=AFc=20Huard?= Date: Fri, 24 Nov 2023 21:59:41 +0100 Subject: [PATCH 12/87] [CONTINT-258] E2e tests: assert tags on logs (#21082) --- test/new-e2e/tests/containers/ecs_test.go | 34 +++++++++++++++++ test/new-e2e/tests/containers/k8s_test.go | 46 ++++++++++++++++++++++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/test/new-e2e/tests/containers/ecs_test.go b/test/new-e2e/tests/containers/ecs_test.go index 78b050d052d03..a351237a799a4 100644 --- a/test/new-e2e/tests/containers/ecs_test.go +++ b/test/new-e2e/tests/containers/ecs_test.go @@ -202,6 +202,24 @@ func (suite *ecsSuite) TestNginx() { Service: "apps-nginx-server", }, Expect: testLogExpectArgs{ + Tags: &[]string{ + `^cluster_name:` + regexp.QuoteMeta(suite.ecsClusterName) + `$`, + `^container_id:`, + `^container_name:ecs-.*-nginx-ec2-`, + `^docker_image:ghcr.io/datadog/apps-nginx-server:main$`, + `^ecs_cluster_name:` + regexp.QuoteMeta(suite.ecsClusterName) + `$`, + `^ecs_container_name:nginx$`, + `^git.commit.sha:`, // org.opencontainers.image.revision docker image label + `^git.repository_url:https://github.com/DataDog/test-infra-definitions$`, // org.opencontainers.image.source docker image label + `^image_id:sha256:`, + `^image_name:ghcr.io/datadog/apps-nginx-server$`, + `^image_tag:main$`, + `^short_image:apps-nginx-server$`, + `^task_arn:arn:`, + `^task_family:.*-nginx-ec2$`, + `^task_name:.*-nginx-ec2$`, + `^task_version:[[:digit:]]+$`, + }, Message: `GET / HTTP/1\.1`, }, }) @@ -242,6 +260,22 @@ func (suite *ecsSuite) TestRedis() { Service: "redis", }, Expect: testLogExpectArgs{ + Tags: &[]string{ + `^cluster_name:` + regexp.QuoteMeta(suite.ecsClusterName) + `$`, + `^container_id:`, + `^container_name:ecs-.*-redis-ec2-`, + `^docker_image:redis:latest$`, + `^ecs_cluster_name:` + regexp.QuoteMeta(suite.ecsClusterName) + `$`, + `^ecs_container_name:redis$`, + `^image_id:sha256:`, + `^image_name:redis$`, + `^image_tag:latest$`, + `^short_image:redis$`, + `^task_arn:arn:`, + `^task_family:.*-redis-ec2$`, + `^task_name:.*-redis-ec2$`, + `^task_version:[[:digit:]]+$`, + }, Message: `oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo`, }, }) diff --git a/test/new-e2e/tests/containers/k8s_test.go b/test/new-e2e/tests/containers/k8s_test.go index ac72a955cdbf8..92371a829e20f 100644 --- a/test/new-e2e/tests/containers/k8s_test.go +++ b/test/new-e2e/tests/containers/k8s_test.go @@ -223,7 +223,7 @@ func (suite *k8sSuite) TestNginx() { `^container_name:nginx$`, `^display_container_name:nginx`, `^git\.commit\.sha:`, // org.opencontainers.image.revision docker image label - `^git\.repository_url:https://github\.com/DataDog/test-infra-definitions$`, // org.opencontainers.image.source docker image label + `^git\.repository_url:https://github\.com/DataDog/test-infra-definitions$`, // org.opencontainers.image.source docker image label `^image_id:ghcr\.io/datadog/apps-nginx-server@sha256:`, `^image_name:ghcr\.io/datadog/apps-nginx-server$`, `^image_tag:main$`, @@ -288,6 +288,29 @@ func (suite *k8sSuite) TestNginx() { Service: "apps-nginx-server", }, Expect: testLogExpectArgs{ + Tags: &[]string{ + `^container_id:`, + `^container_name:nginx$`, + `^dirname:/var/log/pods/workload-nginx_nginx-`, + `^display_container_name:nginx`, + `^filename:[[:digit:]]+.log$`, + `^git\.commit\.sha:`, // org.opencontainers.image.revision docker image label + `^git\.repository_url:https://github\.com/DataDog/test-infra-definitions$`, // org.opencontainers.image.source docker image label + `^image_id:ghcr.io/datadog/apps-nginx-server@sha256:`, + `^image_name:ghcr.io/datadog/apps-nginx-server$`, + `^image_tag:main$`, + `^kube_container_name:nginx$`, + `^kube_deployment:nginx$`, + `^kube_namespace:workload-nginx$`, + `^kube_ownerref_kind:replicaset$`, + `^kube_ownerref_name:nginx-[[:alnum:]]+$`, + `^kube_qos:Burstable$`, + `^kube_replica_set:nginx-[[:alnum:]]+$`, + `^kube_service:nginx$`, + `^pod_name:nginx-[[:alnum:]]+-[[:alnum:]]+$`, + `^pod_phase:running$`, + `^short_image:apps-nginx-server$`, + }, Message: `GET / HTTP/1\.1`, }, }) @@ -358,6 +381,27 @@ func (suite *k8sSuite) TestRedis() { Service: "redis", }, Expect: testLogExpectArgs{ + Tags: &[]string{ + `^container_id:`, + `^container_name:redis$`, + `^dirname:/var/log/pods/workload-redis_redis-`, + `^display_container_name:redis`, + `^filename:[[:digit:]]+.log$`, + `^image_id:docker.io/library/redis@sha256:`, + `^image_name:redis$`, + `^image_tag:latest$`, + `^kube_container_name:redis$`, + `^kube_deployment:redis$`, + `^kube_namespace:workload-redis$`, + `^kube_ownerref_kind:replicaset$`, + `^kube_ownerref_name:redis-[[:alnum:]]+$`, + `^kube_qos:Burstable$`, + `^kube_replica_set:redis-[[:alnum:]]+$`, + `^kube_service:redis$`, + `^pod_name:redis-[[:alnum:]]+-[[:alnum:]]+$`, + `^pod_phase:running$`, + `^short_image:redis$`, + }, Message: `oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo`, }, }) From cdc12a377528f0604eaf8025c8377d2bf504762f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9na=C3=AFc=20Huard?= Date: Fri, 24 Nov 2023 22:03:25 +0100 Subject: [PATCH 13/87] [CONTINT-3310, CONTINT-3314] Check agent restarts at the end of tests (#21096) --- test/new-e2e/tests/containers/k8s_test.go | 24 +++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/test/new-e2e/tests/containers/k8s_test.go b/test/new-e2e/tests/containers/k8s_test.go index 92371a829e20f..d35671279e7da 100644 --- a/test/new-e2e/tests/containers/k8s_test.go +++ b/test/new-e2e/tests/containers/k8s_test.go @@ -78,9 +78,26 @@ func (suite *k8sSuite) TearDownSuite() { // and to have the following tests with a smaller timeout. // // Inside a testify test suite, tests are executed in alphabetical order. -// The 00 in Test00UpAndRunning is here to guarantee that this test, waiting for the agent pods to be ready +// The 00 in Test00UpAndRunning is here to guarantee that this test, waiting for the agent pods to be ready, // is run first. func (suite *k8sSuite) Test00UpAndRunning() { + suite.testUpAndRunning(5 * time.Minute) +} + +// An agent restart (because of a health probe failure or because of a OOM kill for ex.) +// can cause a completely random failure on a completely random test. +// A metric can be fully missing if the agent is restarted when the metric is checked. +// Only a subset of tags can be missing if the agent has just restarted, but not all the +// collectors have finished to feed workload meta and the tagger. +// So, checking if any agent has restarted during the tests can be valuable for investigations. +// +// Inside a testify test suite, tests are executed in alphabetical order. +// The ZZ in TestZZUpAndRunning is here to guarantee that this test, is run last. +func (suite *k8sSuite) TestZZUpAndRunning() { + suite.testUpAndRunning(1 * time.Minute) +} + +func (suite *k8sSuite) testUpAndRunning(waitFor time.Duration) { ctx := context.Background() suite.Run("agent pods are ready and not restarting", func() { @@ -156,9 +173,12 @@ func (suite *k8sSuite) Test00UpAndRunning() { } } } - }, 5*time.Minute, 10*time.Second, "Not all agents eventually became ready in time.") + }, waitFor, 10*time.Second, "Not all agents eventually became ready in time.") }) +} +func (suite *k8sSuite) TestVersion() { + ctx := context.Background() versionExtractor := regexp.MustCompile(`Commit: ([[:xdigit:]]+)`) for _, tt := range []struct { From d7e40ecd2c2dfa8403ef0e77d369e34e13914277 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 08:54:38 +0100 Subject: [PATCH 14/87] CWS: sync BTFhub constants (#21099) Co-authored-by: paulcacheux --- pkg/security/probe/constantfetch/btfhub/constants.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/security/probe/constantfetch/btfhub/constants.json b/pkg/security/probe/constantfetch/btfhub/constants.json index e940c12db3191..18187d76dd199 100644 --- a/pkg/security/probe/constantfetch/btfhub/constants.json +++ b/pkg/security/probe/constantfetch/btfhub/constants.json @@ -1,5 +1,5 @@ { - "commit": "0810ea32e42a7caeeb884d6d34e5d80ab06b5396", + "commit": "ab2b4260f230140be6c99dc5e538e224d8b2dd95", "constants": [ { "binprm_file_offset": 168, @@ -14140,6 +14140,13 @@ "uname_release": "3.10.0-1160.105.1.0.1.el7.x86_64", "cindex": 82 }, + { + "distrib": "ol", + "version": "7", + "arch": "x86_64", + "uname_release": "3.10.0-1160.105.1.0.2.el7.x86_64", + "cindex": 82 + }, { "distrib": "ol", "version": "7", From c03e4cfb2727608cc7e74f4d433f278641b36b9f Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Mon, 27 Nov 2023 10:03:53 +0100 Subject: [PATCH 15/87] [CWS] fix the hostname resolution of CWS/CSPM EvP paylods (#21061) * allow the logs message to override the hostname in the message * override the CWS events hostname using the one we computed in the security agent * fix compliance part as well * add release note * apply review suggestion on release note Co-authored-by: Kari Halsted <12926135+kayayarai@users.noreply.github.com> --------- Co-authored-by: Kari Halsted <12926135+kayayarai@users.noreply.github.com> --- .../subcommands/start/compliance.go | 10 +++++----- cmd/security-agent/subcommands/check/command.go | 10 ++++++++-- .../subcommands/compliance/command.go | 11 +++++++++-- .../subcommands/compliance/compliance.go | 2 +- .../subcommands/runtime/command.go | 2 +- cmd/security-agent/subcommands/start/command.go | 10 ++-------- pkg/compliance/reporter.go | 11 ++++------- pkg/logs/message/message.go | 5 +++++ pkg/security/module/server.go | 4 +++- pkg/security/reporter/reporter.go | 9 ++++++--- pkg/security/utils/hostname.go | 16 ++++++++++++++++ .../csm-events-hostname-42359b25de381a90.yaml | 5 +++++ 12 files changed, 65 insertions(+), 30 deletions(-) create mode 100644 releasenotes/notes/csm-events-hostname-42359b25de381a90.yaml diff --git a/cmd/cluster-agent/subcommands/start/compliance.go b/cmd/cluster-agent/subcommands/start/compliance.go index d4833889c16a0..a6be8344d2225 100644 --- a/cmd/cluster-agent/subcommands/start/compliance.go +++ b/cmd/cluster-agent/subcommands/start/compliance.go @@ -83,19 +83,19 @@ func startCompliance(senderManager sender.SenderManager, stopper startstop.Stopp configDir := coreconfig.Datadog.GetString("compliance_config.dir") checkInterval := coreconfig.Datadog.GetDuration("compliance_config.check_interval") - reporter, err := compliance.NewLogReporter(stopper, "compliance-agent", "compliance", runPath, endpoints, ctx) + hname, err := hostname.Get(context.TODO()) if err != nil { return err } - runner := runner.NewRunner(senderManager) - stopper.Add(runner) - - hname, err := hostname.Get(context.TODO()) + reporter, err := compliance.NewLogReporter(hname, stopper, "compliance-agent", "compliance", runPath, endpoints, ctx) if err != nil { return err } + runner := runner.NewRunner(senderManager) + stopper.Add(runner) + agent := compliance.NewAgent(senderManager, compliance.AgentOptions{ ConfigDir: configDir, Reporter: reporter, diff --git a/cmd/security-agent/subcommands/check/command.go b/cmd/security-agent/subcommands/check/command.go index f724fde693418..ce00b27fddea6 100644 --- a/cmd/security-agent/subcommands/check/command.go +++ b/cmd/security-agent/subcommands/check/command.go @@ -36,6 +36,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/compliance/k8sconfig" pkgconfig "github.com/DataDog/datadog-agent/pkg/config" "github.com/DataDog/datadog-agent/pkg/security/common" + "github.com/DataDog/datadog-agent/pkg/security/utils" "github.com/DataDog/datadog-agent/pkg/util/flavor" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/hostname" @@ -243,7 +244,12 @@ func dumpComplianceEvents(reportFile string, events []*compliance.CheckEvent) er return nil } -func reportComplianceEvents(_ log.Component, config config.Component, events []*compliance.CheckEvent) error { +func reportComplianceEvents(log log.Component, config config.Component, events []*compliance.CheckEvent) error { + hostnameDetected, err := utils.GetHostnameWithContextAndFallback(context.Background()) + if err != nil { + return log.Errorf("Error while getting hostname, exiting: %v", err) + } + stopper := startstop.NewSerialStopper() defer stopper.Stop() runPath := config.GetString("compliance_config.run_path") @@ -251,7 +257,7 @@ func reportComplianceEvents(_ log.Component, config config.Component, events []* if err != nil { return fmt.Errorf("reporter: could not reate log context for compliance: %w", err) } - reporter, err := compliance.NewLogReporter(stopper, "compliance-agent", "compliance", runPath, endpoints, context) + reporter, err := compliance.NewLogReporter(hostnameDetected, stopper, "compliance-agent", "compliance", runPath, endpoints, context) if err != nil { return fmt.Errorf("reporter: could not create: %w", err) } diff --git a/cmd/security-agent/subcommands/compliance/command.go b/cmd/security-agent/subcommands/compliance/command.go index 4136854f1a5a8..080e2638945bd 100644 --- a/cmd/security-agent/subcommands/compliance/command.go +++ b/cmd/security-agent/subcommands/compliance/command.go @@ -6,6 +6,7 @@ package compliance import ( + "context" "fmt" "strings" @@ -21,6 +22,7 @@ import ( "github.com/DataDog/datadog-agent/comp/core/secrets" "github.com/DataDog/datadog-agent/pkg/compliance" "github.com/DataDog/datadog-agent/pkg/security/common" + "github.com/DataDog/datadog-agent/pkg/security/utils" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/startstop" ) @@ -79,7 +81,12 @@ func complianceEventCommand(globalParams *command.GlobalParams) *cobra.Command { return eventCmd } -func eventRun(_ log.Component, config config.Component, _ secrets.Component, eventArgs *cliParams) error { +func eventRun(log log.Component, config config.Component, eventArgs *cliParams) error { + hostnameDetected, err := utils.GetHostnameWithContextAndFallback(context.Background()) + if err != nil { + return log.Errorf("Error while getting hostname, exiting: %v", err) + } + stopper := startstop.NewSerialStopper() defer stopper.Stop() @@ -89,7 +96,7 @@ func eventRun(_ log.Component, config config.Component, _ secrets.Component, eve } runPath := config.GetString("compliance_config.run_path") - reporter, err := compliance.NewLogReporter(stopper, eventArgs.sourceName, eventArgs.sourceType, runPath, endpoints, dstContext) + reporter, err := compliance.NewLogReporter(hostnameDetected, stopper, eventArgs.sourceName, eventArgs.sourceType, runPath, endpoints, dstContext) if err != nil { return fmt.Errorf("failed to set up compliance log reporter: %w", err) } diff --git a/cmd/security-agent/subcommands/compliance/compliance.go b/cmd/security-agent/subcommands/compliance/compliance.go index 50b5beba0aa77..41e4957f5a981 100644 --- a/cmd/security-agent/subcommands/compliance/compliance.go +++ b/cmd/security-agent/subcommands/compliance/compliance.go @@ -44,7 +44,7 @@ func StartCompliance(log log.Component, config config.Component, sysprobeconfig } stopper.Add(context) - reporter, err := compliance.NewLogReporter(stopper, "compliance-agent", "compliance", runPath, endpoints, context) + reporter, err := compliance.NewLogReporter(hostname, stopper, "compliance-agent", "compliance", runPath, endpoints, context) if err != nil { return nil, err } diff --git a/cmd/security-agent/subcommands/runtime/command.go b/cmd/security-agent/subcommands/runtime/command.go index f9c03236a28f2..3b01f562c4c1e 100644 --- a/cmd/security-agent/subcommands/runtime/command.go +++ b/cmd/security-agent/subcommands/runtime/command.go @@ -646,7 +646,7 @@ func StartRuntimeSecurity(log log.Component, config config.Component, hostname s stopper.Add(ctx) runPath := config.GetString("runtime_security_config.run_path") - reporter, err := reporter.NewCWSReporter(runPath, stopper, endpoints, ctx) + reporter, err := reporter.NewCWSReporter(hostname, runPath, stopper, endpoints, ctx) if err != nil { return nil, err } diff --git a/cmd/security-agent/subcommands/start/command.go b/cmd/security-agent/subcommands/start/command.go index d9ce2ecfea6f3..826c5810ee613 100644 --- a/cmd/security-agent/subcommands/start/command.go +++ b/cmd/security-agent/subcommands/start/command.go @@ -51,7 +51,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/tagger/remote" "github.com/DataDog/datadog-agent/pkg/util" "github.com/DataDog/datadog-agent/pkg/util/fxutil" - "github.com/DataDog/datadog-agent/pkg/util/hostname" "github.com/DataDog/datadog-agent/pkg/util/profiling" "github.com/DataDog/datadog-agent/pkg/util/startstop" "github.com/DataDog/datadog-agent/pkg/version" @@ -241,15 +240,10 @@ func RunAgent(ctx context.Context, log log.Component, config config.Component, s } }() - hostnameDetected, err := utils.GetHostnameWithContext(ctx) + hostnameDetected, err := utils.GetHostnameWithContextAndFallback(ctx) if err != nil { - log.Warnf("Could not resolve hostname from core-agent: %v", err) - hostnameDetected, err = hostname.Get(ctx) - if err != nil { - return log.Errorf("Error while getting hostname, exiting: %v", err) - } + return log.Errorf("Error while getting hostname, exiting: %v", err) } - log.Infof("Hostname is: %s", hostnameDetected) demultiplexer.AddAgentStartupTelemetry(fmt.Sprintf("%s - Datadog Security Agent", version.AgentVersion)) stopper = startstop.NewSerialStopper() diff --git a/pkg/compliance/reporter.go b/pkg/compliance/reporter.go index 5fa58073af8a9..5967810ca9de1 100644 --- a/pkg/compliance/reporter.go +++ b/pkg/compliance/reporter.go @@ -6,7 +6,6 @@ package compliance import ( - "context" "encoding/json" "fmt" "strings" @@ -23,13 +22,13 @@ import ( "github.com/DataDog/datadog-agent/pkg/logs/sources" "github.com/DataDog/datadog-agent/pkg/security/common" "github.com/DataDog/datadog-agent/pkg/status/health" - "github.com/DataDog/datadog-agent/pkg/util/hostname" "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/util/startstop" ) // LogReporter is responsible for sending compliance logs to DataDog backends. type LogReporter struct { + hostname string logSource *sources.LogSource logChan chan *message.Message endpoints *config.Endpoints @@ -37,11 +36,7 @@ type LogReporter struct { } // NewLogReporter instantiates a new log LogReporter -func NewLogReporter(stopper startstop.Stopper, sourceName, sourceType, runPath string, endpoints *config.Endpoints, dstcontext *client.DestinationsContext) (*LogReporter, error) { - hostname, err := hostname.Get(context.Background()) - if err != nil || hostname == "" { - hostname = "unknown" - } +func NewLogReporter(hostname string, stopper startstop.Stopper, sourceName, sourceType, runPath string, endpoints *config.Endpoints, dstcontext *client.DestinationsContext) (*LogReporter, error) { health := health.RegisterLiveness(sourceType) // setup the auditor @@ -79,6 +74,7 @@ func NewLogReporter(stopper startstop.Stopper, sourceName, sourceType, runPath s } return &LogReporter{ + hostname: hostname, logSource: logSource, logChan: logChan, endpoints: endpoints, @@ -101,5 +97,6 @@ func (r *LogReporter) ReportEvent(event interface{}) { origin := message.NewOrigin(r.logSource) origin.SetTags(r.tags) msg := message.NewMessage(buf, origin, message.StatusInfo, time.Now().UnixNano()) + msg.Hostname = r.hostname r.logChan <- msg } diff --git a/pkg/logs/message/message.go b/pkg/logs/message/message.go index bfe1179980884..67e30ac80f7b1 100644 --- a/pkg/logs/message/message.go +++ b/pkg/logs/message/message.go @@ -31,6 +31,7 @@ type Payload struct { // Message represents a log line sent to datadog, with its metadata type Message struct { MessageContent + Hostname string Origin *Origin Status string IngestionTimestamp int64 @@ -307,6 +308,10 @@ func (m *Message) GetLatency() int64 { // GetHostname returns the hostname to applied the given log message func (m *Message) GetHostname() string { + if m.Hostname != "" { + return m.Hostname + } + if m.Lambda != nil { return m.Lambda.ARN } diff --git a/pkg/security/module/server.go b/pkg/security/module/server.go index dac0ddd5bdaa7..68e27cfb69bfc 100644 --- a/pkg/security/module/server.go +++ b/pkg/security/module/server.go @@ -482,7 +482,9 @@ func newDirectReporter(stopper startstop.Stopper) (common.RawReporter, error) { log.Info(status) } - reporter, err := reporter.NewCWSReporter(runPath, stopper, endpoints, destinationsCtx) + // we set the hostname to the empty string to take advantage of the out of the box message hostname + // resolution + reporter, err := reporter.NewCWSReporter("", runPath, stopper, endpoints, destinationsCtx) if err != nil { return nil, fmt.Errorf("failed to create direct reporter: %w", err) } diff --git a/pkg/security/reporter/reporter.go b/pkg/security/reporter/reporter.go index fc530ca05d2c1..61bd53b36c4e7 100644 --- a/pkg/security/reporter/reporter.go +++ b/pkg/security/reporter/reporter.go @@ -24,6 +24,7 @@ import ( // RuntimeReporter represents a CWS reporter, used to send events to the intake type RuntimeReporter struct { + hostname string logSource *sources.LogSource logChan chan *message.Message } @@ -34,15 +35,16 @@ func (r *RuntimeReporter) ReportRaw(content []byte, service string, tags ...stri origin.SetTags(tags) origin.SetService(service) msg := message.NewMessage(content, origin, message.StatusInfo, time.Now().UnixNano()) + msg.Hostname = r.hostname r.logChan <- msg } // NewCWSReporter returns a new CWS reported based on the fields necessary to communicate with the intake -func NewCWSReporter(runPath string, stopper startstop.Stopper, endpoints *logsconfig.Endpoints, context *client.DestinationsContext) (seccommon.RawReporter, error) { - return newReporter(runPath, stopper, "runtime-security-agent", "runtime-security", endpoints, context) +func NewCWSReporter(hostname string, runPath string, stopper startstop.Stopper, endpoints *logsconfig.Endpoints, context *client.DestinationsContext) (seccommon.RawReporter, error) { + return newReporter(hostname, runPath, stopper, "runtime-security-agent", "runtime-security", endpoints, context) } -func newReporter(runPath string, stopper startstop.Stopper, sourceName, sourceType string, endpoints *logsconfig.Endpoints, context *client.DestinationsContext) (seccommon.RawReporter, error) { +func newReporter(hostname string, runPath string, stopper startstop.Stopper, sourceName, sourceType string, endpoints *logsconfig.Endpoints, context *client.DestinationsContext) (seccommon.RawReporter, error) { health := health.RegisterLiveness("runtime-security") // setup the auditor @@ -64,6 +66,7 @@ func newReporter(runPath string, stopper startstop.Stopper, sourceName, sourceTy ) logChan := pipelineProvider.NextPipelineChan() return &RuntimeReporter{ + hostname: hostname, logSource: logSource, logChan: logChan, }, nil diff --git a/pkg/security/utils/hostname.go b/pkg/security/utils/hostname.go index 7fc716f2dac44..a23bec4708b23 100644 --- a/pkg/security/utils/hostname.go +++ b/pkg/security/utils/hostname.go @@ -14,6 +14,7 @@ import ( pbgo "github.com/DataDog/datadog-agent/pkg/proto/pbgo/core" "github.com/DataDog/datadog-agent/pkg/util/grpc" + "github.com/DataDog/datadog-agent/pkg/util/hostname" "github.com/DataDog/datadog-agent/pkg/util/log" ) @@ -54,3 +55,18 @@ func GetHostnameWithContext(ctx context.Context) (string, error) { }, retry.LastErrorOnly(true), retry.Attempts(maxAttempts), retry.Context(ctx)) return hostname, err } + +// GetHostnameWithContextAndFallback attempts to acquire a hostname by connecting to the +// core agent's gRPC endpoints extending the given context, or falls back to local resolution +func GetHostnameWithContextAndFallback(ctx context.Context) (string, error) { + hostnameDetected, err := GetHostnameWithContext(ctx) + if err != nil { + log.Warnf("Could not resolve hostname from core-agent: %v", err) + hostnameDetected, err = hostname.Get(ctx) + if err != nil { + return "", err + } + } + log.Infof("Hostname is: %s", hostnameDetected) + return hostnameDetected, nil +} diff --git a/releasenotes/notes/csm-events-hostname-42359b25de381a90.yaml b/releasenotes/notes/csm-events-hostname-42359b25de381a90.yaml new file mode 100644 index 0000000000000..4a62235019b64 --- /dev/null +++ b/releasenotes/notes/csm-events-hostname-42359b25de381a90.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + CWS/CSPM: Fixes the hostname value attached to CWS and CSPM events, which in rare cases + the security agent computed incorrectly. From d0d83686ed7a39dce7a38dd6320f56a059eb2cc9 Mon Sep 17 00:00:00 2001 From: Sylvain Baubeau Date: Mon, 27 Nov 2023 10:07:15 +0100 Subject: [PATCH 16/87] Send SIGHUP when reloading system-probe systemd service (#20778) --- .../config/templates/datadog-agent/systemd.sysprobe.service.erb | 1 + 1 file changed, 1 insertion(+) diff --git a/omnibus/config/templates/datadog-agent/systemd.sysprobe.service.erb b/omnibus/config/templates/datadog-agent/systemd.sysprobe.service.erb index 79e081d1ff133..68dc883843ccf 100644 --- a/omnibus/config/templates/datadog-agent/systemd.sysprobe.service.erb +++ b/omnibus/config/templates/datadog-agent/systemd.sysprobe.service.erb @@ -11,6 +11,7 @@ Type=simple PIDFile=<%= install_dir %>/run/system-probe.pid Restart=on-failure ExecStart=<%= install_dir %>/embedded/bin/system-probe run --config=<%= etc_dir %>/system-probe.yaml --pid=<%= install_dir %>/run/system-probe.pid +ExecReload=/bin/kill -HUP $MAINPID # Since systemd 229, should be in [Unit] but in order to support systemd <229, # it is also supported to have it here. StartLimitInterval=10 From c5f62a504242d760b53b858487a80744e38e689a Mon Sep 17 00:00:00 2001 From: Kacper <89013263+kacper-murzyn@users.noreply.github.com> Date: Mon, 27 Nov 2023 11:24:24 +0100 Subject: [PATCH 17/87] Kacper murzyn/unfreeze task (#21094) * Improve unfreeze task * Test changes * Cleanup after tests * Add commit for .gitlab-ci.yml * Description updated --- tasks/release.py | 76 ++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 42 deletions(-) diff --git a/tasks/release.py b/tasks/release.py index 30f44f3679a49..a473f3db453d8 100644 --- a/tasks/release.py +++ b/tasks/release.py @@ -37,6 +37,12 @@ UNFREEZE_REPO_AGENT = "datadog-agent" UNFREEZE_REPOS = [UNFREEZE_REPO_AGENT, "omnibus-software", "omnibus-ruby", "datadog-agent-macos-build"] +RELEASE_JSON_FIELDS_TO_UPDATE = [ + "INTEGRATIONS_CORE_VERSION", + "OMNIBUS_SOFTWARE_VERSION", + "OMNIBUS_RUBY_VERSION", + "MACOS_BUILD_VERSION", +] @task @@ -1232,7 +1238,7 @@ def _get_release_json_value(key): return release_json -def create_release_branch(ctx, repo, release_branch, base_directory="~/dd", upstream="origin"): +def create_and_update_release_branch(ctx, repo, release_branch, base_directory="~/dd", upstream="origin"): # Perform branch out in all required repositories with ctx.cd(f"{base_directory}/{repo}"): # Step 1 - Create a local branch out from the default branch @@ -1245,11 +1251,31 @@ def create_release_branch(ctx, repo, release_branch, base_directory="~/dd", upst ctx.run(f"git checkout -b {release_branch}") if repo == UNFREEZE_REPO_AGENT: + # Step 1.1 - In datadog-agent repo update base_branch and nightly builds entries rj = _load_release_json() + rj["base_branch"] = release_branch + + for nightly in ["nightly", "nightly-a7"]: + for field in RELEASE_JSON_FIELDS_TO_UPDATE: + rj[nightly][field] = f"{release_branch}" + _save_release_json(rj) - ctx.run("git add release.json") - ok = try_git_command(ctx, f"git commit -m 'Set base_branch to {release_branch}'") + + # Step 1.2 - In datadog-agent repo update gitlab-ci.yaml jobs + with open(".gitlab-ci.yml", "r") as gl: + file_content = gl.readlines() + + with open(".gitlab-ci.yml", "w") as gl: + for line in file_content: + if re.search(r"compare_to: main", line): + gl.write(line.replace("main", f"{release_branch}")) + else: + gl.write(line) + + # Step 1.3 - Commit new changes + ctx.run("git add release.json .gitlab-ci.yml") + ok = try_git_command(ctx, f"git commit -m 'Update release.json and .gitlab-ci.yml with {release_branch}'") if not ok: raise Exit( color_message( @@ -1274,13 +1300,13 @@ def create_release_branch(ctx, repo, release_branch, base_directory="~/dd", upst @task(help={'upstream': "Remote repository name (default 'origin')"}) -def unfreeze(ctx, base_directory="~/dd", major_versions="6,7", upstream="origin", redo=False): +def unfreeze(ctx, base_directory="~/dd", major_versions="6,7", upstream="origin"): """ Performs set of tasks required for the main branch unfreeze during the agent release cycle. That includes: - - creates a release branch in datadog-agent, omnibus-ruby and omnibus-software repositories, - - pushes an empty commit on the datadog-agent main branch, - - creates devel tags in the datadog-agent repository on the empty commit from the last step. + - creates a release branch in datadog-agent, datadog-agent-macos, omnibus-ruby and omnibus-software repositories, + - updates release.json on new datadog-agent branch to point to newly created release branches in nightly section + - updates entries in .gitlab-ci.yml which depend on local branch name Notes: base_directory - path to the directory where dd repos are cloned, defaults to ~/dd, but can be overwritten. @@ -1300,7 +1326,6 @@ def unfreeze(ctx, base_directory="~/dd", major_versions="6,7", upstream="origin" # Strings with proper branch/tag names release_branch = current.branch() - devel_tag = str(next) # Step 0: checks @@ -1317,41 +1342,8 @@ def unfreeze(ctx, base_directory="~/dd", major_versions="6,7", upstream="origin" ): raise Exit(color_message("Aborting.", "red"), code=1) - # Step 1: Create release branch for repo in UNFREEZE_REPOS: - create_release_branch(ctx, repo, release_branch, base_directory=base_directory) - - print(color_message("Creating empty commit for devel tags", "bold")) - with ctx.cd(f"{base_directory}/datadog-agent"): - ctx.run("git checkout main") - ok = try_git_command(ctx, "git commit --allow-empty -m 'Empty commit for next release devel tags'") - if not ok: - raise Exit( - color_message( - "Could not create commit. Please commit manually, push the commit manually to the main branch.", - "red", - ), - code=1, - ) - - print(color_message("Pushing new commit", "bold")) - res = ctx.run(f"git push {upstream}", warn=True) - if res.exited is None or res.exited > 0: - raise Exit( - color_message( - f"Could not push commit to the upstream '{upstream}'. Please push it manually.", - "red", - ), - code=1, - ) - - # Step 3: Create tags for next version - print(color_message(f"Creating devel tags for agent version(s) {list_major_versions}", "bold")) - print( - color_message("If commit signing is enabled, you will have to make sure each tag gets properly signed.", "bold") - ) - - tag_version(ctx, devel_tag, tag_modules=False, push=True, force=redo) + create_and_update_release_branch(ctx, repo, release_branch, base_directory=base_directory, upstream=upstream) @task From 750770c3fb51c15eacb4af55e7801022738434bb Mon Sep 17 00:00:00 2001 From: Nicolas Schweitzer Date: Mon, 27 Nov 2023 12:56:36 +0100 Subject: [PATCH 18/87] Remove windows lint from MQ required jobs (#21104) Remove windows lint from MQ required jobs --- .gitlab/source_test/windows.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitlab/source_test/windows.yml b/.gitlab/source_test/windows.yml index c12ec331aa718..444c9ebe6d06e 100644 --- a/.gitlab/source_test/windows.yml +++ b/.gitlab/source_test/windows.yml @@ -76,12 +76,15 @@ tests_windows-x64-fast: variables: PYTHON_RUNTIMES: 3 ARCH: "x64" - EXTRA_OPTS: '--only-modified-packages' + EXTRA_OPTS: "--only-modified-packages" TEST_OUTPUT_FILE: test_output_fast.json allow_failure: true lint_windows-x64: extends: .lint_windows_base + rules: + - !reference [.except_mergequeue] + - when: on_success variables: PYTHON_RUNTIMES: 3 ARCH: "x64" From 16c849add7c402e6e6cab0f2abc814899ae5a633 Mon Sep 17 00:00:00 2001 From: Kylian Serrania Date: Mon, 27 Nov 2023 13:40:12 +0100 Subject: [PATCH 19/87] [github] Update core checks ownership (#21062) --- .github/CODEOWNERS | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 554434876ac46..2761c2fc79b68 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -280,14 +280,13 @@ /pkg/collector/corechecks/embed/process_agent*.go @Datadog/agent-platform @DataDog/processes /pkg/collector/corechecks/orchestrator/ @DataDog/container-app /pkg/collector/corechecks/kubernetes/ @DataDog/container-integrations -/pkg/collector/corechecks/net/ @DataDog/agent-platform +/pkg/collector/corechecks/net/ @DataDog/platform-integrations /pkg/collector/corechecks/oracle-dbm @DataDog/database-monitoring /pkg/collector/corechecks/sbom/ @DataDog/container-integrations /pkg/collector/corechecks/snmp/ @DataDog/network-device-monitoring -/pkg/collector/corechecks/system/ @DataDog/agent-platform +/pkg/collector/corechecks/system/ @DataDog/platform-integrations +/pkg/collector/corechecks/system/**/*_windows*.go @DataDog/platform-integrations @DataDog/windows-agent /pkg/collector/corechecks/system/wincrashdetect/ @DataDog/windows-kernel-integrations -/pkg/collector/corechecks/system/**/*_windows*.go @DataDog/agent-platform @DataDog/windows-agent -/pkg/collector/corechecks/system/wincrashdetect/ @DataDog/windows-kernel-integrations /pkg/collector/corechecks/system/winkmem/ @DataDog/windows-agent /pkg/collector/corechecks/system/winproc/ @DataDog/windows-agent /pkg/collector/corechecks/systemd/ @DataDog/agent-integrations From 411492b1834223c5fd7fe84621acb2a2118bf513 Mon Sep 17 00:00:00 2001 From: Adel Haj Hassan <41540817+adel121@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:56:39 +0100 Subject: [PATCH 20/87] fix handling processes without pod in PLD client (#21091) --- comp/languagedetection/client/client.go | 18 ++-- comp/languagedetection/client/client_test.go | 96 +++++++++++++++++--- 2 files changed, 95 insertions(+), 19 deletions(-) diff --git a/comp/languagedetection/client/client.go b/comp/languagedetection/client/client.go index 341cf4022d19b..47d30c181d757 100644 --- a/comp/languagedetection/client/client.go +++ b/comp/languagedetection/client/client.go @@ -287,7 +287,6 @@ func (c *client) retryProcessEventsWithoutPod(containerIDs []string) { c.handleProcessEvent(procEvent, true) } } - c.processesWithoutPod = make(map[string]*eventsToRetry) } // handleProcessEvent updates the current batch and the freshlyUpdatedPods @@ -341,6 +340,7 @@ func (c *client) handleProcessEvent(processEvent workloadmeta.Event, isRetry boo added := containerInfo.Add(string(process.Language.Name)) if added { c.freshlyUpdatedPods[pod.Name] = struct{}{} + delete(c.processesWithoutPod, process.ContainerID) } c.telemetry.ProcessedEvents.Inc(pod.Name, containerName, string(process.Language.Name)) } @@ -352,15 +352,17 @@ func (c *client) handlePodEvent(podEvent workloadmeta.Event) { for _, c := range append(pod.InitContainers, pod.Containers...) { containerIDs = append(containerIDs, c.ID) } - if podEvent.Type == workloadmeta.EventTypeSet { - c.retryProcessEventsWithoutPod(containerIDs) - } - for _, cid := range containerIDs { - delete(c.processesWithoutPod, cid) + switch podEvent.Type { + case workloadmeta.EventTypeSet: + c.retryProcessEventsWithoutPod(containerIDs) + case workloadmeta.EventTypeUnset: + delete(c.currentBatch, pod.Name) + delete(c.freshlyUpdatedPods, pod.Name) + for _, cid := range containerIDs { + delete(c.processesWithoutPod, cid) + } } - delete(c.currentBatch, pod.Name) - delete(c.freshlyUpdatedPods, pod.Name) } func (c *client) getCurrentBatchProto() *pbgo.ParentLanguageAnnotationRequest { diff --git a/comp/languagedetection/client/client_test.go b/comp/languagedetection/client/client_test.go index 84e31f0085a54..18d369d9b72f0 100644 --- a/comp/languagedetection/client/client_test.go +++ b/comp/languagedetection/client/client_test.go @@ -784,6 +784,20 @@ func TestRun(t *testing.T) { }, } + container3 := &workloadmeta.Container{ + EntityID: workloadmeta.EntityID{ + ID: "python-cont-id3", + Kind: workloadmeta.KindContainer, + }, + EntityMeta: workloadmeta.EntityMeta{ + Name: "python-cont-name3", + }, + Owner: &workloadmeta.EntityID{ + ID: "python-pod-id3", + Kind: workloadmeta.KindKubernetesPod, + }, + } + pod1 := &workloadmeta.KubernetesPod{ EntityID: workloadmeta.EntityID{ ID: "nginx-pod-id1", @@ -832,6 +846,30 @@ func TestRun(t *testing.T) { }, } + pod3 := &workloadmeta.KubernetesPod{ + EntityID: workloadmeta.EntityID{ + ID: "python-pod-id3", + Kind: workloadmeta.KindKubernetesPod, + }, + EntityMeta: workloadmeta.EntityMeta{ + Name: "python-pod-name3", + Namespace: "python-pod-namespace3", + }, + Containers: []workloadmeta.OrchestratorContainer{ + { + ID: "python-cont-id3", + Name: "python-cont-name3", + }, + }, + Owners: []workloadmeta.KubernetesPodOwner{ + { + ID: "python-replicaset-id3", + Name: "python-replicaset-name3", + Kind: "replicaset", + }, + }, + } + process1 := &workloadmeta.Process{ EntityID: workloadmeta.EntityID{ Kind: workloadmeta.KindProcess, @@ -843,38 +881,38 @@ func TestRun(t *testing.T) { ContainerID: "nginx-cont-id1", } - processWithoutPod := &workloadmeta.Process{ + process2 := &workloadmeta.Process{ EntityID: workloadmeta.EntityID{ Kind: workloadmeta.KindProcess, - ID: "1234", + ID: "012", }, Language: &languagemodels.Language{ - Name: "java", + Name: "go", }, - ContainerID: "unknown-container", + ContainerID: "nginx-cont-id2", } - process2 := &workloadmeta.Process{ + process3 := &workloadmeta.Process{ EntityID: workloadmeta.EntityID{ Kind: workloadmeta.KindProcess, - ID: "012", + ID: "1234", }, Language: &languagemodels.Language{ - Name: "go", + Name: "python", }, - ContainerID: "nginx-cont-id2", + ContainerID: "python-cont-id3", } collectorEvents1 := []workloadmeta.CollectorEvent{ { Type: workloadmeta.EventTypeSet, Source: workloadmeta.SourceAll, - Entity: pod1, + Entity: container1, }, { Type: workloadmeta.EventTypeSet, Source: workloadmeta.SourceAll, - Entity: container1, + Entity: pod1, }, { Type: workloadmeta.EventTypeSet, @@ -884,7 +922,13 @@ func TestRun(t *testing.T) { { Type: workloadmeta.EventTypeSet, Source: workloadmeta.SourceAll, - Entity: processWithoutPod, + Entity: container1, + }, + // Process 3 set event is here received before the set event of container 3 and pod 3 + { + Type: workloadmeta.EventTypeSet, + Source: workloadmeta.SourceAll, + Entity: process3, }, } @@ -926,6 +970,18 @@ func TestRun(t *testing.T) { Source: workloadmeta.SourceAll, Entity: process2, }, + // Now we receive the set events of container 3 and pod 3. + // This should lead to retrying processing the process set event + { + Type: workloadmeta.EventTypeSet, + Source: workloadmeta.SourceAll, + Entity: container3, + }, + { + Type: workloadmeta.EventTypeSet, + Source: workloadmeta.SourceAll, + Entity: pod3, + }, } client.store.Notify(collectorEvents2) @@ -957,6 +1013,19 @@ func TestRun(t *testing.T) { Kind: "replicaset", }, }, + "python-pod-name3": { + namespace: "python-pod-namespace3", + containerInfo: langUtil.ContainersLanguages{ + "python-cont-name3": { + "python": {}, + }, + }, + ownerRef: &workloadmeta.KubernetesPodOwner{ + ID: "python-replicaset-id3", + Name: "python-replicaset-name3", + Kind: "replicaset", + }, + }, } // the periodic flush mechanism should send the entire data every 100ms @@ -975,6 +1044,11 @@ func TestRun(t *testing.T) { Source: workloadmeta.SourceAll, Entity: pod2, }, + { + Type: workloadmeta.EventTypeUnset, + Source: workloadmeta.SourceAll, + Entity: pod3, + }, } client.store.Notify(unsetPodEvent) From e39cc498eeed06ae22f6157f8c966bffda9c495d Mon Sep 17 00:00:00 2001 From: Julio Guerra Date: Mon, 27 Nov 2023 15:52:37 +0100 Subject: [PATCH 21/87] pkg/serverless/appsec: prevent flaky tests (#21107) --- pkg/serverless/appsec/appsec_test.go | 7 +++++-- pkg/serverless/appsec/httpsec/proxy_test.go | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pkg/serverless/appsec/appsec_test.go b/pkg/serverless/appsec/appsec_test.go index e87d447149dcd..253a59f79cc0e 100644 --- a/pkg/serverless/appsec/appsec_test.go +++ b/pkg/serverless/appsec/appsec_test.go @@ -8,6 +8,7 @@ package appsec import ( "encoding/json" "fmt" + "os" "strconv" "testing" @@ -16,6 +17,10 @@ import ( "github.com/stretchr/testify/require" ) +func init() { + os.Setenv("DD_APPSEC_WAF_TIMEOUT", "1m") +} + func TestNew(t *testing.T) { for _, appsecEnabled := range []bool{true, false} { appsecEnabledStr := strconv.FormatBool(appsecEnabled) @@ -50,7 +55,6 @@ func TestMonitor(t *testing.T) { t.Run("events-detection", func(t *testing.T) { t.Setenv("DD_SERVERLESS_APPSEC_ENABLED", "true") - t.Setenv("DD_APPSEC_WAF_TIMEOUT", "2s") asm, err := newAppSec() require.NoError(t, err) defer asm.Close() @@ -78,7 +82,6 @@ func TestMonitor(t *testing.T) { t.Setenv("DD_API_SECURITY_REQUEST_SAMPLE_RATE", "1.0") t.Setenv("DD_EXPERIMENTAL_API_SECURITY_ENABLED", "true") t.Setenv("DD_SERVERLESS_APPSEC_ENABLED", "true") - t.Setenv("DD_APPSEC_WAF_TIMEOUT", "2s") asm, err := newAppSec() require.NoError(t, err) defer asm.Close() diff --git a/pkg/serverless/appsec/httpsec/proxy_test.go b/pkg/serverless/appsec/httpsec/proxy_test.go index 4ebb71585f529..78addd5af1a71 100644 --- a/pkg/serverless/appsec/httpsec/proxy_test.go +++ b/pkg/serverless/appsec/httpsec/proxy_test.go @@ -20,6 +20,10 @@ import ( "github.com/stretchr/testify/require" ) +func init() { + os.Setenv("DD_APPSEC_WAF_TIMEOUT", "1m") +} + func TestProxyLifecycleProcessor(t *testing.T) { t.Setenv("DD_SERVERLESS_APPSEC_ENABLED", "true") lp, err := appsec.New() From e8cc667a72e4cc331392e11a9075d9257a777534 Mon Sep 17 00:00:00 2001 From: Alexandre Menasria <47357713+amenasria@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:55:58 +0100 Subject: [PATCH 22/87] Change ugly emote (#21112) [Slapr] Change `approved` emote --- .github/workflows/slapr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/slapr.yml b/.github/workflows/slapr.yml index 6c469e6159c54..f04b355b08936 100644 --- a/.github/workflows/slapr.yml +++ b/.github/workflows/slapr.yml @@ -22,7 +22,7 @@ jobs: SLACK_API_TOKEN: "${{ secrets.SLACK_API_TOKEN }}" SLAPR_BOT_USER_ID: "${{ secrets.SLAPR_BOT_USER_ID }}" SLAPR_EMOJI_REVIEW_STARTED: "review_started" - SLAPR_EMOJI_APPROVED: "approved2" + SLAPR_EMOJI_APPROVED: "gh-approved" SLAPR_EMOJI_CHANGES_REQUESTED: "changes_requested" SLAPR_EMOJI_MERGED: "merged" SLAPR_EMOJI_CLOSED: "closed" From 47c22c583d5052555ef169cc811a471760f96dd6 Mon Sep 17 00:00:00 2001 From: Kevin Fairise <132568982+KevinFairise2@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:52:43 +0100 Subject: [PATCH 23/87] APL-2316 Support different flavor of the agent, and move kitchen to new e2e for debian (#20957) Co-authored-by: pducolin <45568537+pducolin@users.noreply.github.com> --- .gitlab/e2e_test_junit_upload.yml | 3 + .gitlab/kitchen_testing/debian.yml | 17 ----- .gitlab/kitchen_tests_upload.yml | 3 - .gitlab/new-e2e_testing/debian.yml | 42 +++++++++++ tasks/new_e2e_tests.py | 6 +- .../agent-platform/common/agent_behaviour.go | 67 ++++++++++++++++++ .../agent-platform/common/agent_install.go | 10 +-- .../agent-platform/common/helper/unix.go | 33 +++++++++ .../agent-platform/common/test_client.go | 2 + .../install-script/install_script_test.go | 69 +++++++++++++++++-- .../tests/agent-platform/install/install.go | 5 ++ .../install/installparams/install_params.go | 8 +++ 12 files changed, 233 insertions(+), 32 deletions(-) diff --git a/.gitlab/e2e_test_junit_upload.yml b/.gitlab/e2e_test_junit_upload.yml index 588cc4db3e830..ee901fb168f15 100644 --- a/.gitlab/e2e_test_junit_upload.yml +++ b/.gitlab/e2e_test_junit_upload.yml @@ -16,6 +16,9 @@ e2e_test_junit_upload: - new-e2e-language-detection-main - new-e2e-agent-platform-install-script-debian-a7-x64 - new-e2e-agent-platform-install-script-debian-a7-arm64 + - new-e2e-agent-platform-install-script-debian-iot-agent-a7-x86_64 + - new-e2e-agent-platform-install-script-debian-dogstatsd-a7-x86_64 + - new-e2e-agent-platform-install-script-debian-heroku-agent-a7-x86_64 - new-e2e-npm-main script: - set +x diff --git a/.gitlab/kitchen_testing/debian.yml b/.gitlab/kitchen_testing/debian.yml index dc81c95592d1e..6d3b5203a80a3 100644 --- a/.gitlab/kitchen_testing/debian.yml +++ b/.gitlab/kitchen_testing/debian.yml @@ -78,28 +78,11 @@ kitchen_debian_install_script_agent-a6_arm64: - .kitchen_scenario_debian_a6_arm64 - .kitchen_test_install_script_agent -kitchen_debian_install_script_iot_agent-a7: - rules: - !reference [.on_default_kitchen_tests_a7] - extends: - - .kitchen_scenario_debian_a7_x64 - - .kitchen_test_install_script_iot_agent - kitchen_debian_install_script_heroku_agent-a6: extends: - .kitchen_scenario_debian_a6_x64 - .kitchen_test_install_script_heroku_agent -kitchen_debian_install_script_heroku_agent-a7: - extends: - - .kitchen_scenario_debian_a7_x64 - - .kitchen_test_install_script_heroku_agent - -kitchen_debian_install_script_dogstatsd-a7: - extends: - - .kitchen_scenario_debian_a7_x64 - - .kitchen_test_install_script_dogstatsd - # We only want to run step-by-step tests on deploy pipelines, # which is why they have a different rule (if_deploy_6/7) diff --git a/.gitlab/kitchen_tests_upload.yml b/.gitlab/kitchen_tests_upload.yml index 140da94df94b9..ef2982f377c00 100644 --- a/.gitlab/kitchen_tests_upload.yml +++ b/.gitlab/kitchen_tests_upload.yml @@ -32,10 +32,7 @@ kitchen_tests_upload_common: - kitchen_centos_upgrade7_iot_agent-a7 - kitchen_debian_install_script_agent-a6_arm64 - kitchen_debian_install_script_agent-a6_x64 - - kitchen_debian_install_script_dogstatsd-a7 - kitchen_debian_install_script_heroku_agent-a6 - - kitchen_debian_install_script_heroku_agent-a7 - - kitchen_debian_install_script_iot_agent-a7 - kitchen_debian_process_agent-a7 - kitchen_debian_upgrade5_agent-a6 - kitchen_debian_upgrade5_agent-a7 diff --git a/.gitlab/new-e2e_testing/debian.yml b/.gitlab/new-e2e_testing/debian.yml index 4989459fc00f8..5c4094de0e638 100644 --- a/.gitlab/new-e2e_testing/debian.yml +++ b/.gitlab/new-e2e_testing/debian.yml @@ -45,3 +45,45 @@ new-e2e-agent-platform-install-script-debian-a7-arm64: TEAM: agent-platform EXTRA_PARAMS: --osversion $E2E_OSVERS --platform $E2E_PLATFORM --cws-supported-osversion $E2E_CWS_SUPPORTED_OSVERS --arch arm64 +new-e2e-agent-platform-install-script-debian-iot-agent-a7-x86_64: + stage: kitchen_testing + extends: + - .new_e2e_template + - .new-e2e_os_debian + - .new-e2e_debian_a7_x64 + - .new-e2e_agent_a7 + rules: + !reference [.on_default_new-e2e_tests_a7] + variables: + TARGETS: ./tests/agent-platform/install-script + TEAM: agent-platform + EXTRA_PARAMS: --osversion $E2E_OSVERS --platform $E2E_PLATFORM --cws-supported-osversion $E2E_CWS_SUPPORTED_OSVERS --arch x86_64 --flavor datadog-iot-agent + +new-e2e-agent-platform-install-script-debian-dogstatsd-a7-x86_64: + stage: kitchen_testing + extends: + - .new_e2e_template + - .new-e2e_os_debian + - .new-e2e_debian_a7_x64 + - .new-e2e_agent_a7 + variables: + TARGETS: ./tests/agent-platform/install-script + TEAM: agent-platform + EXTRA_PARAMS: --osversion $E2E_OSVERS --platform $E2E_PLATFORM --cws-supported-osversion $E2E_CWS_SUPPORTED_OSVERS --arch x86_64 --flavor datadog-dogstatsd + +new-e2e-agent-platform-install-script-debian-heroku-agent-a7-x86_64: + stage: kitchen_testing + extends: + - .new_e2e_template + - .new-e2e_os_debian + - .new-e2e_debian_a7_x64 + - .new-e2e_agent_a7 + variables: + TARGETS: ./tests/agent-platform/install-script + TEAM: agent-platform + EXTRA_PARAMS: --osversion $E2E_OSVERS --platform $E2E_PLATFORM --cws-supported-osversion $E2E_CWS_SUPPORTED_OSVERS --arch x86_64 --flavor datadog-heroku-agent + + + + + diff --git a/tasks/new_e2e_tests.py b/tasks/new_e2e_tests.py index d4294372ec81c..cafc4dfb1ffe1 100644 --- a/tasks/new_e2e_tests.py +++ b/tasks/new_e2e_tests.py @@ -45,6 +45,8 @@ def run( osversion="", platform="", arch="", + flavor="", + major_version="", cws_supported_osversion="", keep_stacks=False, cache=False, @@ -91,7 +93,7 @@ def run( test_run_arg = f"-run {test_run_name}" cmd = f'gotestsum --format {gotestsum_format} ' - cmd += '{junit_file_flag} --packages="{packages}" -- -ldflags="-X {REPO_PATH}/test/new-e2e/tests/containers.GitCommit={commit}" {verbose} -mod={go_mod} -vet=off -timeout {timeout} -tags {go_build_tags} {nocache} {run} {skip} {coverage_opt} {test_run_arg} -args {osversion} {platform} {arch} {cws_supported_osversion} {keep_stacks}' + cmd += '{junit_file_flag} --packages="{packages}" -- -ldflags="-X {REPO_PATH}/test/new-e2e/tests/containers.GitCommit={commit}" {verbose} -mod={go_mod} -vet=off -timeout {timeout} -tags {go_build_tags} {nocache} {run} {skip} {coverage_opt} {test_run_arg} -args {osversion} {platform} {major_version} {arch} {flavor} {cws_supported_osversion} {keep_stacks}' args = { "go_mod": "mod", @@ -107,6 +109,8 @@ def run( "osversion": f"-osversion {osversion}" if osversion else '', "platform": f"-platform {platform}" if platform else '', "arch": f"-arch {arch}" if arch else '', + "flavor": f"-flavor {flavor}" if flavor else '', + "major_version": f"-major-version {major_version}" if major_version else '', "cws_supported_osversion": f"-cws-supported-osversion {cws_supported_osversion}" if cws_supported_osversion else '', diff --git a/test/new-e2e/tests/agent-platform/common/agent_behaviour.go b/test/new-e2e/tests/agent-platform/common/agent_behaviour.go index 9c70da51cb487..79d5ce4af7698 100644 --- a/test/new-e2e/tests/agent-platform/common/agent_behaviour.go +++ b/test/new-e2e/tests/agent-platform/common/agent_behaviour.go @@ -58,6 +58,20 @@ func CheckAgentBehaviour(t *testing.T, client *TestClient) { }) } +// CheckDogstatdAgentBehaviour runs tests to check the agent behave properly with dogstatsd +func CheckDogstatdAgentBehaviour(t *testing.T, client *TestClient) { + t.Run("dogstatsd service running", func(tt *testing.T) { + _, err := client.SvcManager.Status("datadog-dogstatsd") + require.NoError(tt, err, "dogstatsd service should be running") + }) + + t.Run("dogstatsd config file exists", func(tt *testing.T) { + _, err := client.FileManager.FileExists(fmt.Sprintf("%s/%s", client.Helper.GetConfigFolder(), "dogstatsd.yaml")) + require.NoError(tt, err, "dogstatsd config file should be present") + }) + +} + // CheckAgentStops runs tests to check the agent can stop properly func CheckAgentStops(t *testing.T, client *TestClient) { t.Run("stops", func(tt *testing.T) { @@ -90,6 +104,33 @@ func CheckAgentStops(t *testing.T, client *TestClient) { }) } +// CheckDogstatsdAgentStops runs tests to check the agent can stop properly +func CheckDogstatsdAgentStops(t *testing.T, client *TestClient) { + t.Run("stops", func(tt *testing.T) { + _, err := client.SvcManager.Stop("datadog-dogstatsd") + require.NoError(tt, err) + + _, err = client.SvcManager.Status("datadog-dogstatsd") + require.Error(tt, err, "datadog-dogstatsd service should be stopped") + }) + + t.Run("no running processes", func(tt *testing.T) { + dogstatsdProcesses := []string{"datadog-dogstatsd"} + for _, process := range dogstatsdProcesses { + _, err := client.VMClient.ExecuteWithError(fmt.Sprintf("pgrep -f %s", process)) + require.Error(tt, err, fmt.Sprintf("process %s should not be running", process)) + } + }) + + t.Run("starts after stop", func(tt *testing.T) { + _, err := client.SvcManager.Start("datadog-dogstatsd") + require.NoError(tt, err) + + _, err = client.SvcManager.Status("datadog-dogstatsd") + require.NoError(tt, err, "datadog-dogstatsd should be running") + }) +} + // CheckAgentRestarts runs tests to check the agent can restart properly func CheckAgentRestarts(t *testing.T, client *TestClient) { @@ -116,6 +157,32 @@ func CheckAgentRestarts(t *testing.T, client *TestClient) { }) } +// CheckDogstatsdAgentRestarts runs tests to check the agent can restart properly +func CheckDogstatsdAgentRestarts(t *testing.T, client *TestClient) { + + t.Run("restart when stopped", func(tt *testing.T) { + _, err := client.SvcManager.Stop("datadog-dogstatsd") + require.NoError(tt, err) + + _, err = client.SvcManager.Restart("datadog-dogstatsd") + require.NoError(tt, err) + + _, err = client.SvcManager.Status("datadog-dogstatsd") + require.NoError(tt, err, "datadog-dogstatsd should restart when stopped") + }) + + t.Run("restart when running", func(tt *testing.T) { + _, err := client.SvcManager.Start("datadog-dogstatsd") + require.NoError(tt, err) + + _, err = client.SvcManager.Restart("datadog-dogstatsd") + require.NoError(tt, err) + + _, err = client.SvcManager.Status("datadog-dogstatsd") + require.NoError(tt, err, "datadog-dogstatsd should restart when running") + }) +} + // CheckAgentPython runs tests to check the agent use the correct python version func CheckAgentPython(t *testing.T, client *TestClient, version string) { t.Run(fmt.Sprintf("set python version %s and restarts", version), func(tt *testing.T) { diff --git a/test/new-e2e/tests/agent-platform/common/agent_install.go b/test/new-e2e/tests/agent-platform/common/agent_install.go index 59060591f26fa..0586df80bc564 100644 --- a/test/new-e2e/tests/agent-platform/common/agent_install.go +++ b/test/new-e2e/tests/agent-platform/common/agent_install.go @@ -19,7 +19,7 @@ func CheckInstallation(t *testing.T, client *TestClient) { t.Run("example config file", func(tt *testing.T) { - exampleFilePath := client.Helper.GetConfigFolder() + "datadog.yaml.example" + exampleFilePath := client.Helper.GetConfigFolder() + fmt.Sprintf("%s.example", client.Helper.GetConfigFileName()) _, err := client.FileManager.FileExists(exampleFilePath) require.NoError(tt, err, "Example config file should be present") @@ -46,7 +46,7 @@ func CheckInstallation(t *testing.T, client *TestClient) { func CheckInstallationInstallScript(t *testing.T, client *TestClient) { t.Run("site config attribute", func(tt *testing.T) { - configFilePath := client.Helper.GetConfigFolder() + "datadog.yaml" + configFilePath := client.Helper.GetConfigFolder() + client.Helper.GetConfigFileName() var configYAML map[string]any config, err := client.FileManager.ReadFile(configFilePath) @@ -78,15 +78,15 @@ func CheckInstallationInstallScript(t *testing.T, client *TestClient) { } // CheckUninstallation runs check to see if the agent uninstall properly -func CheckUninstallation(t *testing.T, client *TestClient) { +func CheckUninstallation(t *testing.T, client *TestClient, packageName string) { t.Run("remove the agent", func(tt *testing.T) { - _, err := client.PkgManager.Remove("datadog-agent") + _, err := client.PkgManager.Remove(packageName) require.NoError(tt, err, "should uninstall the agent") }) t.Run("no agent process running", func(tt *testing.T) { - agentProcesses := []string{"datadog-agent", "system-probe", "security-agent"} + agentProcesses := []string{client.Helper.GetServiceName(), "system-probe", "security-agent"} for _, process := range agentProcesses { _, err := client.VMClient.ExecuteWithError(fmt.Sprintf("pgrep -f %s", process)) require.Error(tt, err, fmt.Sprintf("process %s should not be running", process)) diff --git a/test/new-e2e/tests/agent-platform/common/helper/unix.go b/test/new-e2e/tests/agent-platform/common/helper/unix.go index 42bd9cf5bdfe5..81ac9cc8e3329 100644 --- a/test/new-e2e/tests/agent-platform/common/helper/unix.go +++ b/test/new-e2e/tests/agent-platform/common/helper/unix.go @@ -6,9 +6,13 @@ // Package helper implement interfaces to get some information that can be OS specific package helper +import "github.com/DataDog/datadog-agent/test/new-e2e/tests/agent-platform/common" + // Unix implement helper function for Unix distributions type Unix struct{} +var _ common.Helper = (*Unix)(nil) + // NewUnixHelper create a new instance of Unix helper func NewUnixHelper() *Unix { return &Unix{} } @@ -20,3 +24,32 @@ func (u *Unix) GetConfigFolder() string { return "/etc/datadog-agent/" } // GetBinaryPath return the datadog-agent binary path func (u *Unix) GetBinaryPath() string { return "/usr/bin/datadog-agent" } + +// GetConfigFileName return the config file name +func (u *Unix) GetConfigFileName() string { return "datadog.yaml" } + +// GetServiceName return the service name +func (u *Unix) GetServiceName() string { return "datadog-agent" } + +// UnixDogstatsd implement helper function for Dogstatsd on Unix distributions +type UnixDogstatsd struct{} + +var _ common.Helper = (*UnixDogstatsd)(nil) + +// NewUnixDogstatsdHelper create a new instance of Unix helper for dogstatsd +func NewUnixDogstatsdHelper() *UnixDogstatsd { return &UnixDogstatsd{} } + +// GetInstallFolder return the install folder path +func (u *UnixDogstatsd) GetInstallFolder() string { return "/opt/datadog-dogstatsd/" } + +// GetConfigFolder return the config folder path +func (u *UnixDogstatsd) GetConfigFolder() string { return "/etc/datadog-dogstatsd/" } + +// GetBinaryPath return the datadog-agent binary path +func (u *UnixDogstatsd) GetBinaryPath() string { return "/usr/bin/datadog-dogstatsd" } + +// GetConfigFileName return the config file name +func (u *UnixDogstatsd) GetConfigFileName() string { return "dogstatsd.yaml" } + +// GetServiceName return the service name +func (u *UnixDogstatsd) GetServiceName() string { return "datadog-dogstatsd" } diff --git a/test/new-e2e/tests/agent-platform/common/test_client.go b/test/new-e2e/tests/agent-platform/common/test_client.go index 4a3df56de3eac..e842e73f2f54f 100644 --- a/test/new-e2e/tests/agent-platform/common/test_client.go +++ b/test/new-e2e/tests/agent-platform/common/test_client.go @@ -43,6 +43,8 @@ type Helper interface { GetInstallFolder() string GetConfigFolder() string GetBinaryPath() string + GetConfigFileName() string + GetServiceName() string } func getServiceManager(vmClient e2eClient.VM) ServiceManager { diff --git a/test/new-e2e/tests/agent-platform/install-script/install_script_test.go b/test/new-e2e/tests/agent-platform/install-script/install_script_test.go index e73699fc19bc5..576eacd1f0f56 100644 --- a/test/new-e2e/tests/agent-platform/install-script/install_script_test.go +++ b/test/new-e2e/tests/agent-platform/install-script/install_script_test.go @@ -32,7 +32,8 @@ import ( var osVersion = flag.String("osversion", "", "os version to test") var platform = flag.String("platform", "", "platform to test") var cwsSupportedOsVersion = flag.String("cws-supported-osversion", "", "list of os where CWS is supported") -var architecture = flag.String("arch", "", "architecture to test (x86_64, arm64))") +var architecture = flag.String("arch", "x84_64", "architecture to test (x86_64, arm64))") +var flavor = flag.String("flavor", "datadog-agent", "flavor to test (datadog-agent, datadog-iot-agent, datadog-dogstatsd, datadog-fips-proxy, datadog-heroku-agent)") type installScriptSuite struct { e2e.Suite[e2e.VMEnv] @@ -74,23 +75,38 @@ func TestInstallScript(t *testing.T) { } } - t.Run(fmt.Sprintf("test install script on %s %s", osVers, *architecture), func(tt *testing.T) { + t.Run(fmt.Sprintf("test install script on %s %s %s", osVers, *architecture, *flavor), func(tt *testing.T) { tt.Parallel() fmt.Printf("Testing %s", osVers) - e2e.Run(tt, &installScriptSuite{cwsSupported: cwsSupported}, e2e.EC2VMStackDef(ec2params.WithImageName(platformJSON[*platform][*architecture][osVers], archMapping[*architecture], osMapping[*platform])), params.WithStackName(fmt.Sprintf("install-script-test-%v-%v-%s", os.Getenv("CI_PIPELINE_ID"), osVers, *architecture))) + e2e.Run(tt, &installScriptSuite{cwsSupported: cwsSupported}, e2e.EC2VMStackDef(ec2params.WithImageName(platformJSON[*platform][*architecture][osVers], archMapping[*architecture], osMapping[*platform])), params.WithStackName(fmt.Sprintf("install-script-test-%v-%v-%s-%s", os.Getenv("CI_PIPELINE_ID"), osVers, *architecture, *flavor))) }) } } func (is *installScriptSuite) TestInstallAgent() { + switch *flavor { + case "datadog-agent": + is.AgentTest() + case "datadog-heroku-agent": + is.AgentTest() + case "datadog-iot-agent": + is.IotAgentTest() + case "datadog-dogstatsd": + is.DogstatsdAgentTest() + } +} + +func (is *installScriptSuite) AgentTest() { fileManager := filemanager.NewUnixFileManager(is.Env().VM) - unixHelper := helpers.NewUnixHelper() + vm := is.Env().VM.(*client.PulumiStackVM) agentClient, err := client.NewAgentClient(is.T(), vm, vm.GetOS(), false) require.NoError(is.T(), err) + + unixHelper := helpers.NewUnixHelper() client := common.NewTestClient(is.Env().VM, agentClient, fileManager, unixHelper) - install.Unix(is.T(), client, installparams.WithArch(*architecture)) + install.Unix(is.T(), client, installparams.WithArch(*architecture), installparams.WithFlavor(*flavor)) common.CheckInstallation(is.T(), client) common.CheckAgentBehaviour(is.T(), client) @@ -104,6 +120,47 @@ func (is *installScriptSuite) TestInstallAgent() { common.CheckCWSBehaviour(is.T(), client) } common.CheckInstallationInstallScript(is.T(), client) - common.CheckUninstallation(is.T(), client) + common.CheckUninstallation(is.T(), client, "datadog-agent") + +} + +func (is *installScriptSuite) IotAgentTest() { + fileManager := filemanager.NewUnixFileManager(is.Env().VM) + + vm := is.Env().VM.(*client.PulumiStackVM) + agentClient, err := client.NewAgentClient(is.T(), vm, vm.GetOS(), false) + require.NoError(is.T(), err) + + unixHelper := helpers.NewUnixHelper() + client := common.NewTestClient(is.Env().VM, agentClient, fileManager, unixHelper) + + install.Unix(is.T(), client, installparams.WithArch(*architecture), installparams.WithFlavor(*flavor)) + + common.CheckInstallation(is.T(), client) + common.CheckAgentBehaviour(is.T(), client) + common.CheckAgentStops(is.T(), client) + common.CheckAgentRestarts(is.T(), client) + + common.CheckInstallationInstallScript(is.T(), client) + common.CheckUninstallation(is.T(), client, "datadog-iot-agent") +} + +func (is *installScriptSuite) DogstatsdAgentTest() { + fileManager := filemanager.NewUnixFileManager(is.Env().VM) + vm := is.Env().VM.(*client.PulumiStackVM) + agentClient, err := client.NewAgentClient(is.T(), vm, vm.GetOS(), false) + require.NoError(is.T(), err) + + unixHelper := helpers.NewUnixDogstatsdHelper() + client := common.NewTestClient(is.Env().VM, agentClient, fileManager, unixHelper) + + install.Unix(is.T(), client, installparams.WithArch(*architecture), installparams.WithFlavor(*flavor)) + + common.CheckInstallation(is.T(), client) + common.CheckDogstatdAgentBehaviour(is.T(), client) + common.CheckDogstatsdAgentStops(is.T(), client) + common.CheckDogstatsdAgentRestarts(is.T(), client) + common.CheckInstallationInstallScript(is.T(), client) + common.CheckUninstallation(is.T(), client, "datadog-dogstatsd") } diff --git a/test/new-e2e/tests/agent-platform/install/install.go b/test/new-e2e/tests/agent-platform/install/install.go index e9a359697f8f5..3d1abfdaf5a43 100644 --- a/test/new-e2e/tests/agent-platform/install/install.go +++ b/test/new-e2e/tests/agent-platform/install/install.go @@ -21,6 +21,7 @@ func Unix(t *testing.T, client *common.TestClient, options ...installparams.Opti params := installparams.NewParams(options...) commandLine := "" + if params.PipelineID != "" { testEnvVars := []string{} testEnvVars = append(testEnvVars, "TESTING_APT_URL=apttesting.datad0g.com") @@ -36,6 +37,10 @@ func Unix(t *testing.T, client *common.TestClient, options ...installparams.Opti commandLine = fmt.Sprintf("DD_AGENT_MAJOR_VERSION=%s", params.MajorVersion) } + if params.Flavor != "" { + commandLine += fmt.Sprintf(" DD_AGENT_FLAVOR=%s ", params.Flavor) + } + t.Run("Installing the agent", func(tt *testing.T) { cmd := fmt.Sprintf(`DD_API_KEY="aaaaaaaaaa" %v DD_SITE="datadoghq.eu" bash -c "$(curl -L https://s3.amazonaws.com/dd-agent/scripts/install_script_agent7.sh)"`, commandLine) output, err := client.VMClient.ExecuteWithError(cmd) diff --git a/test/new-e2e/tests/agent-platform/install/installparams/install_params.go b/test/new-e2e/tests/agent-platform/install/installparams/install_params.go index dc136bfd84da2..b70b1c911b854 100644 --- a/test/new-e2e/tests/agent-platform/install/installparams/install_params.go +++ b/test/new-e2e/tests/agent-platform/install/installparams/install_params.go @@ -19,6 +19,7 @@ type Params struct { PipelineID string MajorVersion string Arch string + Flavor string } // Option alias to a functional option changing a given Params instance @@ -54,3 +55,10 @@ func WithArch(arch string) Option { p.Arch = arch } } + +// WithFlavor specify the flavor to use when installing the agent +func WithFlavor(flavor string) Option { + return func(p *Params) { + p.Flavor = flavor + } +} From 4138997d6c25aad96bf86c13ac9bd6537da2d7e9 Mon Sep 17 00:00:00 2001 From: Sylvain Afchain Date: Mon, 27 Nov 2023 16:55:48 +0100 Subject: [PATCH 24/87] [CWS] introduce eBPF less support (#21088) * ebpf probe compiles * ebpfless compiles * decouple resolvers * restore ebpf less probe * prepare windows * rename to ebpf * revert probe_linux * add common probe * adapt windows * fix windows probe * fix scrubber * fix windows handlers * fix linters * rename ebpf resolvers * again linters * restore event tags * post rebase * add missing build tag * fix macos and stress tests * fix again linter * fix macos --- docs/cloud-workload-security/secl.json | 17 + pkg/config/system_probe_cws.go | 4 + pkg/network/events/monitor.go | 4 +- pkg/security/config/config.go | 9 + pkg/security/module/cws_linux.go | 6 +- pkg/security/module/server.go | 2 +- pkg/security/module/server_linux.go | 119 +- pkg/security/probe/custom_events.go | 6 +- pkg/security/probe/discarders_linux.go | 4 +- pkg/security/probe/field_handlers.go | 93 +- ...ndlers_linux.go => field_handlers_ebpf.go} | 201 +- pkg/security/probe/field_handlers_ebpfless.go | 147 ++ pkg/security/probe/field_handlers_windows.go | 21 +- pkg/security/probe/model.go | 11 - .../probe/{model_linux.go => model_ebpf.go} | 13 +- pkg/security/probe/model_ebpfless.go | 41 + pkg/security/probe/model_test.go | 12 +- pkg/security/probe/model_windows.go | 27 +- pkg/security/probe/opts_linux.go | 2 + pkg/security/probe/probe.go | 178 +- pkg/security/probe/probe_ebpf.go | 1924 ++++++++++++++++ ...probe_linux_test.go => probe_ebpf_test.go} | 0 pkg/security/probe/probe_epbfless.go | 246 +++ pkg/security/probe/probe_linux.go | 1943 +---------------- pkg/security/probe/probe_monitor.go | 58 +- pkg/security/probe/probe_others.go | 6 - pkg/security/probe/probe_windows.go | 202 +- pkg/security/probe/security_profile.go | 14 +- pkg/security/probe/selftests/tester_linux.go | 4 +- pkg/security/proto/{api => }/README.md | 0 pkg/security/proto/ebpfless/service.pb.go | 803 +++++++ pkg/security/proto/ebpfless/service.proto | 63 + .../proto/ebpfless/service_grpc.pb.go | 109 + .../proto/ebpfless/service_vtproto.pb.go | 1726 +++++++++++++++ pkg/security/proto/ebpfless/vt_grpc.go | 72 + pkg/security/resolvers/opts_linux.go | 17 + pkg/security/resolvers/process/opts_linux.go | 34 + .../{resolver_others.go => opts_windows.go} | 14 +- pkg/security/resolvers/process/resolver.go | 4 +- .../{resolver_linux.go => resolver_ebpf.go} | 147 +- .../resolvers/process/resolver_ebpfless.go | 234 ++ .../resolvers/process/resolver_test.go | 26 +- .../resolvers/process/resolver_windows.go | 14 +- .../{resolvers_linux.go => resolvers_ebpf.go} | 30 +- pkg/security/resolvers/resolvers_ebpfless.go | 76 + pkg/security/resolvers/resolvers_others.go | 24 - .../generators/accessors/accessors.go | 79 +- .../generators/accessors/accessors.tmpl | 32 +- .../generators/accessors/common/types.go | 24 +- .../compiler/generators/accessors/doc/doc.go | 4 + .../generators/accessors/field_accessors.tmpl | 35 +- .../generators/accessors/field_handlers.tmpl | 22 +- pkg/security/secl/model/accessors_unix.go | 70 +- pkg/security/secl/model/accessors_windows.go | 197 +- pkg/security/secl/model/args_envs.go | 4 +- .../secl/model/field_accessors_unix.go | 858 ++++---- .../secl/model/field_accessors_windows.go | 211 +- .../secl/model/field_handlers_unix.go | 44 +- .../secl/model/field_handlers_windows.go | 25 +- pkg/security/secl/model/model.go | 26 +- pkg/security/secl/model/model_unix.go | 54 +- pkg/security/secl/model/model_windows.go | 8 +- .../activity_tree/activity_tree.go | 16 +- .../activity_tree/activity_tree_graph.go | 4 +- .../activity_tree/file_node.go | 6 +- .../activity_tree/process_node.go | 6 +- pkg/security/security_profile/dump/graph.go | 2 +- pkg/security/security_profile/dump/manager.go | 4 +- .../security_profile/profile/manager.go | 4 +- pkg/security/serializers/serializers_base.go | 7 +- pkg/security/serializers/serializers_linux.go | 78 +- .../serializers/serializers_linux_easyjson.go | 6 +- .../serializers/serializers_others.go | 7 +- .../serializers/serializers_windows.go | 19 +- pkg/security/tests/dentry_test.go | 103 +- pkg/security/tests/module_tester.go | 95 +- pkg/security/tests/mount_test.go | 8 +- pkg/security/tests/network_device_test.go | 10 +- pkg/security/tests/process_test.go | 29 +- pkg/security/tests/sbom_test.go | 8 +- pkg/security/tests/serializers_test.go | 2 +- pkg/security/tests/stress_test.go | 16 +- tasks/security_agent.py | 5 + 83 files changed, 7378 insertions(+), 3457 deletions(-) rename pkg/security/probe/{field_handlers_linux.go => field_handlers_ebpf.go} (59%) create mode 100644 pkg/security/probe/field_handlers_ebpfless.go rename pkg/security/probe/{model_linux.go => model_ebpf.go} (79%) create mode 100644 pkg/security/probe/model_ebpfless.go create mode 100644 pkg/security/probe/probe_ebpf.go rename pkg/security/probe/{probe_linux_test.go => probe_ebpf_test.go} (100%) create mode 100644 pkg/security/probe/probe_epbfless.go rename pkg/security/proto/{api => }/README.md (100%) create mode 100644 pkg/security/proto/ebpfless/service.pb.go create mode 100644 pkg/security/proto/ebpfless/service.proto create mode 100644 pkg/security/proto/ebpfless/service_grpc.pb.go create mode 100644 pkg/security/proto/ebpfless/service_vtproto.pb.go create mode 100644 pkg/security/proto/ebpfless/vt_grpc.go create mode 100644 pkg/security/resolvers/opts_linux.go create mode 100644 pkg/security/resolvers/process/opts_linux.go rename pkg/security/resolvers/process/{resolver_others.go => opts_windows.go} (58%) rename pkg/security/resolvers/process/{resolver_linux.go => resolver_ebpf.go} (87%) create mode 100644 pkg/security/resolvers/process/resolver_ebpfless.go rename pkg/security/resolvers/{resolvers_linux.go => resolvers_ebpf.go} (90%) create mode 100644 pkg/security/resolvers/resolvers_ebpfless.go delete mode 100644 pkg/security/resolvers/resolvers_others.go diff --git a/docs/cloud-workload-security/secl.json b/docs/cloud-workload-security/secl.json index 20d3d9e14ee86..4b282e4a560d4 100644 --- a/docs/cloud-workload-security/secl.json +++ b/docs/cloud-workload-security/secl.json @@ -3009,6 +3009,11 @@ "definition": "Return value of the syscall", "property_doc_link": "common-syscallevent-retval-doc" }, + { + "name": "mount.root.path", + "definition": "Root path of the mount", + "property_doc_link": "mount-root-path-doc" + }, { "name": "mount.source.path", "definition": "Source path of a bind mount", @@ -8879,6 +8884,18 @@ "constants_link": "", "examples": [] }, + { + "name": "mount.root.path", + "link": "mount-root-path-doc", + "type": "string", + "definition": "Root path of the mount", + "prefixes": [ + "mount" + ], + "constants": "", + "constants_link": "", + "examples": [] + }, { "name": "mount.source.path", "link": "mount-source-path-doc", diff --git a/pkg/config/system_probe_cws.go b/pkg/config/system_probe_cws.go index e3a91df1c60ff..353a8e0749644 100644 --- a/pkg/config/system_probe_cws.go +++ b/pkg/config/system_probe_cws.go @@ -97,4 +97,8 @@ func initCWSSystemProbeConfig(cfg Config) { // CWS - UserSessions cfg.BindEnvAndSetDefault("runtime_security_config.user_sessions.cache_size", 1024) + + // CWS -eBPF Less + cfg.BindEnvAndSetDefault("runtime_security_config.ebpfless.enabled", false) + cfg.BindEnvAndSetDefault("runtime_security_config.ebpfless.socket", "localhost:5678") } diff --git a/pkg/network/events/monitor.go b/pkg/network/events/monitor.go index f2e570a420720..552feead6d39e 100644 --- a/pkg/network/events/monitor.go +++ b/pkg/network/events/monitor.go @@ -110,11 +110,13 @@ func (h *eventHandlerWrapper) Copy(ev *model.Event) any { processStartTime = ev.GetProcessForkTime() } + envs := ev.GetProcessEnvp() + return &Process{ Pid: ev.GetProcessPid(), ContainerID: intern.GetByString(ev.GetContainerId()), StartTime: processStartTime.UnixNano(), - Envs: ev.GetProcessEnvp(map[string]bool{ + Envs: model.FilterEnvs(envs, map[string]bool{ "DD_SERVICE": true, "DD_VERSION": true, "DD_ENV": true, diff --git a/pkg/security/config/config.go b/pkg/security/config/config.go index a6a3d0b0d6d28..5a6d88539c7f8 100644 --- a/pkg/security/config/config.go +++ b/pkg/security/config/config.go @@ -205,6 +205,11 @@ type RuntimeSecurityConfig struct { // UserSessionsCacheSize defines the size of the User Sessions cache size UserSessionsCacheSize int + + // EBPFLessEnabled enables the ebpfless probe + EBPFLessEnabled bool + // EBPFLessSocket defines the socket used for the communication between system-probe and the ebpfless source + EBPFLessSocket string } // Config defines a security config @@ -331,6 +336,10 @@ func NewRuntimeSecurityConfig() (*RuntimeSecurityConfig, error) { // User Sessions UserSessionsCacheSize: coreconfig.SystemProbe.GetInt("runtime_security_config.user_sessions.cache_size"), + + // ebpf less + EBPFLessEnabled: coreconfig.SystemProbe.GetBool("runtime_security_config.ebpfless.enabled"), + EBPFLessSocket: coreconfig.SystemProbe.GetString("runtime_security_config.ebpfless.socket"), } if err := rsConfig.sanitize(); err != nil { diff --git a/pkg/security/module/cws_linux.go b/pkg/security/module/cws_linux.go index c7062fc8f6983..4461174af2c10 100644 --- a/pkg/security/module/cws_linux.go +++ b/pkg/security/module/cws_linux.go @@ -8,6 +8,7 @@ package module import ( "github.com/DataDog/datadog-agent/pkg/eventmonitor" "github.com/DataDog/datadog-agent/pkg/security/config" + "github.com/DataDog/datadog-agent/pkg/security/probe" ) // UpdateEventMonitorOpts adapt the event monitor options @@ -15,6 +16,7 @@ func UpdateEventMonitorOpts(opts *eventmonitor.Opts, config *config.Config) { opts.ProbeOpts.PathResolutionEnabled = true opts.ProbeOpts.TTYFallbackEnabled = true opts.ProbeOpts.SyscallsMonitorEnabled = config.Probe.SyscallsMonitorEnabled + opts.ProbeOpts.EBPFLessEnabled = config.RuntimeSecurity.EBPFLessEnabled } // DisableRuntimeSecurity disables all the runtime security features @@ -27,7 +29,9 @@ func DisableRuntimeSecurity(config *config.Config) { // platform specific init function func (c *CWSConsumer) init(evm *eventmonitor.EventMonitor, config *config.RuntimeSecurityConfig, opts Opts) error { //nolint:revive // TODO fix revive unused-parameter // Activity dumps related - evm.Probe.AddActivityDumpHandler(c) + if p, ok := evm.Probe.PlatformProbe.(*probe.EBPFProbe); ok { + p.AddActivityDumpHandler(c) + } return nil } diff --git a/pkg/security/module/server.go b/pkg/security/module/server.go index 68e27cfb69bfc..57ffae033a63f 100644 --- a/pkg/security/module/server.go +++ b/pkg/security/module/server.go @@ -321,7 +321,7 @@ func (a *APIServer) SendEvent(rule *rules.Rule, e events.Event, extTagsCb func() func marshalEvent(event events.Event, probe *sprobe.Probe) ([]byte, error) { if ev, ok := event.(*model.Event); ok { - return serializers.MarshalEvent(ev, probe.GetResolvers()) + return serializers.MarshalEvent(ev) } if ev, ok := event.(events.EventMarshaler); ok { diff --git a/pkg/security/module/server_linux.go b/pkg/security/module/server_linux.go index 1c2f7db76f34f..8c3fb6c697ea2 100644 --- a/pkg/security/module/server_linux.go +++ b/pkg/security/module/server_linux.go @@ -12,13 +12,14 @@ import ( "errors" "fmt" + "github.com/DataDog/datadog-agent/pkg/security/probe" "github.com/DataDog/datadog-agent/pkg/security/proto/api" "github.com/DataDog/datadog-agent/pkg/security/seclog" "github.com/DataDog/datadog-agent/pkg/util/kernel" ) // DumpDiscarders handles discarder dump requests -func (a *APIServer) DumpDiscarders(ctx context.Context, params *api.DumpDiscardersParams) (*api.DumpDiscardersMessage, error) { //nolint:revive // TODO fix revive unused-parameter +func (a *APIServer) DumpDiscarders(_ context.Context, _ *api.DumpDiscardersParams) (*api.DumpDiscardersMessage, error) { filePath, err := a.probe.DumpDiscarders() if err != nil { return nil, err @@ -29,10 +30,8 @@ func (a *APIServer) DumpDiscarders(ctx context.Context, params *api.DumpDiscarde } // DumpProcessCache handles process cache dump requests -func (a *APIServer) DumpProcessCache(ctx context.Context, params *api.DumpProcessCacheParams) (*api.SecurityDumpProcessCacheMessage, error) { //nolint:revive // TODO fix revive unused-parameter - resolvers := a.probe.GetResolvers() - - filename, err := resolvers.ProcessResolver.Dump(params.WithArgs) +func (a *APIServer) DumpProcessCache(_ context.Context, params *api.DumpProcessCacheParams) (*api.SecurityDumpProcessCacheMessage, error) { + filename, err := a.probe.DumpProcessCache(params.WithArgs) if err != nil { return nil, err } @@ -43,8 +42,13 @@ func (a *APIServer) DumpProcessCache(ctx context.Context, params *api.DumpProces } // DumpActivity handle an activity dump request -func (a *APIServer) DumpActivity(ctx context.Context, params *api.ActivityDumpParams) (*api.ActivityDumpMessage, error) { //nolint:revive // TODO fix revive unused-parameter - if managers := a.probe.GetProfileManagers(); managers != nil { +func (a *APIServer) DumpActivity(_ context.Context, params *api.ActivityDumpParams) (*api.ActivityDumpMessage, error) { + p, ok := a.probe.PlatformProbe.(*probe.EBPFProbe) + if !ok { + return nil, fmt.Errorf("not supported") + } + + if managers := p.GetProfileManagers(); managers != nil { msg, err := managers.DumpActivity(params) if err != nil { seclog.Errorf(err.Error()) @@ -56,8 +60,13 @@ func (a *APIServer) DumpActivity(ctx context.Context, params *api.ActivityDumpPa } // ListActivityDumps returns the list of active dumps -func (a *APIServer) ListActivityDumps(ctx context.Context, params *api.ActivityDumpListParams) (*api.ActivityDumpListMessage, error) { //nolint:revive // TODO fix revive unused-parameter - if managers := a.probe.GetProfileManagers(); managers != nil { +func (a *APIServer) ListActivityDumps(_ context.Context, params *api.ActivityDumpListParams) (*api.ActivityDumpListMessage, error) { + p, ok := a.probe.PlatformProbe.(*probe.EBPFProbe) + if !ok { + return nil, fmt.Errorf("not supported") + } + + if managers := p.GetProfileManagers(); managers != nil { msg, err := managers.ListActivityDumps(params) if err != nil { seclog.Errorf(err.Error()) @@ -69,8 +78,13 @@ func (a *APIServer) ListActivityDumps(ctx context.Context, params *api.ActivityD } // StopActivityDump stops an active activity dump if it exists -func (a *APIServer) StopActivityDump(ctx context.Context, params *api.ActivityDumpStopParams) (*api.ActivityDumpStopMessage, error) { //nolint:revive // TODO fix revive unused-parameter - if managers := a.probe.GetProfileManagers(); managers != nil { +func (a *APIServer) StopActivityDump(_ context.Context, params *api.ActivityDumpStopParams) (*api.ActivityDumpStopMessage, error) { + p, ok := a.probe.PlatformProbe.(*probe.EBPFProbe) + if !ok { + return nil, fmt.Errorf("not supported") + } + + if managers := p.GetProfileManagers(); managers != nil { msg, err := managers.StopActivityDump(params) if err != nil { seclog.Errorf(err.Error()) @@ -82,8 +96,13 @@ func (a *APIServer) StopActivityDump(ctx context.Context, params *api.ActivityDu } // TranscodingRequest encodes an activity dump following the requested parameters -func (a *APIServer) TranscodingRequest(ctx context.Context, params *api.TranscodingRequestParams) (*api.TranscodingRequestMessage, error) { //nolint:revive // TODO fix revive unused-parameter - if managers := a.probe.GetProfileManagers(); managers != nil { +func (a *APIServer) TranscodingRequest(_ context.Context, params *api.TranscodingRequestParams) (*api.TranscodingRequestMessage, error) { + p, ok := a.probe.PlatformProbe.(*probe.EBPFProbe) + if !ok { + return nil, fmt.Errorf("not supported") + } + + if managers := p.GetProfileManagers(); managers != nil { msg, err := managers.GenerateTranscoding(params) if err != nil { seclog.Errorf(err.Error()) @@ -95,8 +114,13 @@ func (a *APIServer) TranscodingRequest(ctx context.Context, params *api.Transcod } // ListSecurityProfiles returns the list of security profiles -func (a *APIServer) ListSecurityProfiles(ctx context.Context, params *api.SecurityProfileListParams) (*api.SecurityProfileListMessage, error) { //nolint:revive // TODO fix revive unused-parameter - if managers := a.probe.GetProfileManagers(); managers != nil { +func (a *APIServer) ListSecurityProfiles(_ context.Context, params *api.SecurityProfileListParams) (*api.SecurityProfileListMessage, error) { + p, ok := a.probe.PlatformProbe.(*probe.EBPFProbe) + if !ok { + return nil, fmt.Errorf("not supported") + } + + if managers := p.GetProfileManagers(); managers != nil { msg, err := managers.ListSecurityProfiles(params) if err != nil { seclog.Errorf(err.Error()) @@ -108,8 +132,13 @@ func (a *APIServer) ListSecurityProfiles(ctx context.Context, params *api.Securi } // SaveSecurityProfile saves the requested security profile to disk -func (a *APIServer) SaveSecurityProfile(ctx context.Context, params *api.SecurityProfileSaveParams) (*api.SecurityProfileSaveMessage, error) { //nolint:revive // TODO fix revive unused-parameter - if managers := a.probe.GetProfileManagers(); managers != nil { +func (a *APIServer) SaveSecurityProfile(_ context.Context, params *api.SecurityProfileSaveParams) (*api.SecurityProfileSaveMessage, error) { + p, ok := a.probe.PlatformProbe.(*probe.EBPFProbe) + if !ok { + return nil, fmt.Errorf("not supported") + } + + if managers := p.GetProfileManagers(); managers != nil { msg, err := managers.SaveSecurityProfile(params) if err != nil { seclog.Errorf(err.Error()) @@ -121,32 +150,39 @@ func (a *APIServer) SaveSecurityProfile(ctx context.Context, params *api.Securit } // GetStatus returns the status of the module -func (a *APIServer) GetStatus(ctx context.Context, params *api.GetStatusParams) (*api.Status, error) { //nolint:revive // TODO fix revive unused-parameter - status, err := a.probe.GetConstantFetcherStatus() - if err != nil { - return nil, err +func (a *APIServer) GetStatus(_ context.Context, _ *api.GetStatusParams) (*api.Status, error) { + apiStatus := &api.Status{ + SelfTests: a.selfTester.GetStatus(), } - constants := make([]*api.ConstantValueAndSource, 0, len(status.Values)) - for _, v := range status.Values { - constants = append(constants, &api.ConstantValueAndSource{ - ID: v.ID, - Value: v.Value, - Source: v.FetcherName, - }) - } + p, ok := a.probe.PlatformProbe.(*probe.EBPFProbe) + if ok { + status, err := p.GetConstantFetcherStatus() + if err != nil { + return nil, err + } - apiStatus := &api.Status{ - Environment: &api.EnvironmentStatus{ + constants := make([]*api.ConstantValueAndSource, 0, len(status.Values)) + for _, v := range status.Values { + constants = append(constants, &api.ConstantValueAndSource{ + ID: v.ID, + Value: v.Value, + Source: v.FetcherName, + }) + } + + apiStatus.Environment = &api.EnvironmentStatus{ Constants: &api.ConstantFetcherStatus{ Fetchers: status.Fetchers, Values: constants, }, - }, - SelfTests: a.selfTester.GetStatus(), + KernelLockdown: string(kernel.GetLockdownMode()), + UseMmapableMaps: p.GetKernelVersion().HaveMmapableMaps(), + UseRingBuffer: p.UseRingBuffers(), + } } - envErrors := a.probe.VerifyEnvironment() + envErrors := p.VerifyEnvironment() if envErrors != nil { apiStatus.Environment.Warnings = make([]string, len(envErrors.Errors)) for i, err := range envErrors.Errors { @@ -154,20 +190,21 @@ func (a *APIServer) GetStatus(ctx context.Context, params *api.GetStatusParams) } } - apiStatus.Environment.KernelLockdown = string(kernel.GetLockdownMode()) - apiStatus.Environment.UseMmapableMaps = a.probe.GetKernelVersion().HaveMmapableMaps() - apiStatus.Environment.UseRingBuffer = a.probe.UseRingBuffers() - return apiStatus, nil } // DumpNetworkNamespace handles network namespace cache dump requests -func (a *APIServer) DumpNetworkNamespace(ctx context.Context, params *api.DumpNetworkNamespaceParams) (*api.DumpNetworkNamespaceMessage, error) { //nolint:revive // TODO fix revive unused-parameter - return a.probe.GetResolvers().NamespaceResolver.DumpNetworkNamespaces(params), nil +func (a *APIServer) DumpNetworkNamespace(_ context.Context, params *api.DumpNetworkNamespaceParams) (*api.DumpNetworkNamespaceMessage, error) { + p, ok := a.probe.PlatformProbe.(*probe.EBPFProbe) + if !ok { + return nil, fmt.Errorf("not supported") + } + + return p.Resolvers.NamespaceResolver.DumpNetworkNamespaces(params), nil } // RunSelfTest runs self test and then reload the current policies -func (a *APIServer) RunSelfTest(ctx context.Context, params *api.RunSelfTestParams) (*api.SecuritySelfTestResultMessage, error) { //nolint:revive // TODO fix revive unused-parameter +func (a *APIServer) RunSelfTest(_ context.Context, _ *api.RunSelfTestParams) (*api.SecuritySelfTestResultMessage, error) { if a.cwsConsumer == nil { return nil, errors.New("failed to found module in APIServer") } diff --git a/pkg/security/probe/custom_events.go b/pkg/security/probe/custom_events.go index 7982299d99b92..5556080dfb4f2 100644 --- a/pkg/security/probe/custom_events.go +++ b/pkg/security/probe/custom_events.go @@ -90,15 +90,15 @@ func (a AbnormalEvent) ToJSON() ([]byte, error) { } // NewAbnormalEvent returns the rule and a populated custom event for a abnormal event -func NewAbnormalEvent(id string, description string, event *model.Event, probe *Probe, err error) (*rules.Rule, *events.CustomEvent) { +func NewAbnormalEvent(id string, description string, event *model.Event, err error) (*rules.Rule, *events.CustomEvent) { marshalerCtor := func() events.EventMarshaler { evt := AbnormalEvent{ - Event: serializers.NewEventSerializer(event, probe.resolvers), + Event: serializers.NewEventSerializer(event), Error: err.Error(), } evt.FillCustomEventCommonFields() // Overwrite common timestamp with event timestamp - evt.Timestamp = event.FieldHandlers.ResolveEventTime(event) + evt.Timestamp = event.ResolveEventTime() return evt } diff --git a/pkg/security/probe/discarders_linux.go b/pkg/security/probe/discarders_linux.go index 49e3cd446ca42..335a7df2e60f3 100644 --- a/pkg/security/probe/discarders_linux.go +++ b/pkg/security/probe/discarders_linux.go @@ -55,7 +55,7 @@ var ( recentlyAddedTimeout = uint64(2 * time.Second.Nanoseconds()) ) -type onDiscarderHandler func(rs *rules.RuleSet, event *model.Event, probe *Probe, discarder Discarder) (bool, error) +type onDiscarderHandler func(rs *rules.RuleSet, event *model.Event, probe *EBPFProbe, discarder Discarder) (bool, error) var ( allDiscarderHandlers = make(map[eval.EventType][]onDiscarderHandler) @@ -446,7 +446,7 @@ func (id *inodeDiscarders) discardParentInode(req *erpc.Request, rs *rules.RuleS type inodeEventGetter = func(event *model.Event) (eval.Field, *model.FileEvent, bool) func filenameDiscarderWrapper(eventType model.EventType, getter inodeEventGetter) onDiscarderHandler { - return func(rs *rules.RuleSet, event *model.Event, probe *Probe, discarder Discarder) (bool, error) { + return func(rs *rules.RuleSet, event *model.Event, probe *EBPFProbe, discarder Discarder) (bool, error) { field, fileEvent, isDeleted := getter(event) if fileEvent.PathResolutionError != nil { diff --git a/pkg/security/probe/field_handlers.go b/pkg/security/probe/field_handlers.go index 5f9ea48b7ac5c..942b154d116a3 100644 --- a/pkg/security/probe/field_handlers.go +++ b/pkg/security/probe/field_handlers.go @@ -12,20 +12,9 @@ import ( "sort" "strings" - "github.com/DataDog/datadog-agent/pkg/security/resolvers" "github.com/DataDog/datadog-agent/pkg/security/secl/model" ) -// FieldHandlers defines a field handlers -type FieldHandlers struct { - resolvers *resolvers.Resolvers -} - -// ResolveEventTimestamp resolves the monolitic kernel event timestamp to an absolute time -func (fh *FieldHandlers) ResolveEventTimestamp(ev *model.Event, e *model.BaseEvent) int { - return int(fh.ResolveEventTime(ev).UnixNano()) -} - func bestGuessServiceTag(serviceValues []string) string { if len(serviceValues) == 0 { return "" @@ -51,13 +40,8 @@ func bestGuessServiceTag(serviceValues []string) string { return serviceValues[0] } -// GetProcessService returns the service tag based on the process context -func (fh *FieldHandlers) GetProcessService(ev *model.Event) string { - entry, _ := fh.ResolveProcessCacheEntry(ev) - if entry == nil { - return "" - } - +// getProcessService returns the service tag based on the process context +func getProcessService(entry *model.ProcessCacheEntry) string { var serviceValues []string // first search in the process context itself @@ -84,76 +68,3 @@ func (fh *FieldHandlers) GetProcessService(ev *model.Event) string { return bestGuessServiceTag(serviceValues) } - -// ResolveContainerID resolves the container ID of the event -func (fh *FieldHandlers) ResolveContainerID(ev *model.Event, e *model.ContainerContext) string { - if len(e.ID) == 0 { - if entry, _ := fh.ResolveProcessCacheEntry(ev); entry != nil { - e.ID = entry.ContainerID - } - } - return e.ID -} - -// ResolveContainerCreatedAt resolves the container creation time of the event -func (fh *FieldHandlers) ResolveContainerCreatedAt(ev *model.Event, e *model.ContainerContext) int { - if e.CreatedAt == 0 { - if containerContext, _ := fh.ResolveContainerContext(ev); containerContext != nil { - e.CreatedAt = containerContext.CreatedAt - } - } - return int(e.CreatedAt) -} - -// ResolveContainerTags resolves the container tags of the event -func (fh *FieldHandlers) ResolveContainerTags(_ *model.Event, e *model.ContainerContext) []string { - if len(e.Tags) == 0 && e.ID != "" { - e.Tags = fh.resolvers.TagsResolver.Resolve(e.ID) - } - return e.Tags -} - -// ResolveProcessCreatedAt resolves process creation time -func (fh *FieldHandlers) ResolveProcessCreatedAt(_ *model.Event, e *model.Process) int { - return int(e.ExecTime.UnixNano()) -} - -// ResolveK8SUsername resolves the k8s username of the event -func (fh *FieldHandlers) ResolveK8SUsername(_ *model.Event, evtCtx *model.UserSessionContext) string { - if !evtCtx.Resolved { - if ctx := fh.resolvers.UserSessions.ResolveUserSession(evtCtx.ID); ctx != nil { - *evtCtx = *ctx - } - } - return evtCtx.K8SUsername -} - -// ResolveK8SUID resolves the k8s UID of the event -func (fh *FieldHandlers) ResolveK8SUID(_ *model.Event, evtCtx *model.UserSessionContext) string { - if !evtCtx.Resolved { - if ctx := fh.resolvers.UserSessions.ResolveUserSession(evtCtx.ID); ctx != nil { - *evtCtx = *ctx - } - } - return evtCtx.K8SUID -} - -// ResolveK8SGroups resolves the k8s groups of the event -func (fh *FieldHandlers) ResolveK8SGroups(_ *model.Event, evtCtx *model.UserSessionContext) []string { - if !evtCtx.Resolved { - if ctx := fh.resolvers.UserSessions.ResolveUserSession(evtCtx.ID); ctx != nil { - *evtCtx = *ctx - } - } - return evtCtx.K8SGroups -} - -// ResolveK8SExtra resolves the k8s extra of the event -func (fh *FieldHandlers) ResolveK8SExtra(_ *model.Event, evtCtx *model.UserSessionContext) map[string][]string { - if !evtCtx.Resolved { - if ctx := fh.resolvers.UserSessions.ResolveUserSession(evtCtx.ID); ctx != nil { - *evtCtx = *ctx - } - } - return evtCtx.K8SExtra -} diff --git a/pkg/security/probe/field_handlers_linux.go b/pkg/security/probe/field_handlers_ebpf.go similarity index 59% rename from pkg/security/probe/field_handlers_linux.go rename to pkg/security/probe/field_handlers_ebpf.go index 9fed217d89869..d84f914907b73 100644 --- a/pkg/security/probe/field_handlers_linux.go +++ b/pkg/security/probe/field_handlers_ebpf.go @@ -3,6 +3,8 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. +//go:build linux + // Package probe holds probe related files package probe @@ -12,14 +14,20 @@ import ( "syscall" "time" + "github.com/DataDog/datadog-agent/pkg/security/resolvers" sprocess "github.com/DataDog/datadog-agent/pkg/security/resolvers/process" "github.com/DataDog/datadog-agent/pkg/security/secl/args" "github.com/DataDog/datadog-agent/pkg/security/secl/model" ) +// EBPFFieldHandlers defines a field handlers +type EBPFFieldHandlers struct { + resolvers *resolvers.EBPFResolvers +} + // ResolveProcessCacheEntry queries the ProcessResolver to retrieve the ProcessContext of the event -func (fh *FieldHandlers) ResolveProcessCacheEntry(ev *model.Event) (*model.ProcessCacheEntry, bool) { +func (fh *EBPFFieldHandlers) ResolveProcessCacheEntry(ev *model.Event) (*model.ProcessCacheEntry, bool) { if ev.PIDContext.IsKworker { return model.GetPlaceholderProcessCacheEntry(ev.PIDContext.Pid, ev.PIDContext.Tid, true), false } @@ -37,7 +45,7 @@ func (fh *FieldHandlers) ResolveProcessCacheEntry(ev *model.Event) (*model.Proce } // ResolveFilePath resolves the inode to a full path -func (fh *FieldHandlers) ResolveFilePath(ev *model.Event, f *model.FileEvent) string { +func (fh *EBPFFieldHandlers) ResolveFilePath(ev *model.Event, f *model.FileEvent) string { if !f.IsPathnameStrResolved && len(f.PathnameStr) == 0 { path, err := fh.resolvers.PathResolver.ResolveFileFieldsPath(&f.FileFields, &ev.PIDContext, ev.ContainerContext) if err != nil { @@ -50,7 +58,7 @@ func (fh *FieldHandlers) ResolveFilePath(ev *model.Event, f *model.FileEvent) st } // ResolveFileBasename resolves the inode to a full path -func (fh *FieldHandlers) ResolveFileBasename(ev *model.Event, f *model.FileEvent) string { +func (fh *EBPFFieldHandlers) ResolveFileBasename(_ *model.Event, f *model.FileEvent) string { if !f.IsBasenameStrResolved && len(f.BasenameStr) == 0 { if f.PathnameStr != "" { f.SetBasenameStr(path.Base(f.PathnameStr)) @@ -62,7 +70,7 @@ func (fh *FieldHandlers) ResolveFileBasename(ev *model.Event, f *model.FileEvent } // ResolveFileFilesystem resolves the filesystem a file resides in -func (fh *FieldHandlers) ResolveFileFilesystem(ev *model.Event, f *model.FileEvent) string { +func (fh *EBPFFieldHandlers) ResolveFileFilesystem(ev *model.Event, f *model.FileEvent) string { if f.Filesystem == "" { if f.IsFileless() { f.Filesystem = model.TmpFS @@ -78,22 +86,22 @@ func (fh *FieldHandlers) ResolveFileFilesystem(ev *model.Event, f *model.FileEve } // ResolveProcessArgsFlags resolves the arguments flags of the event -func (fh *FieldHandlers) ResolveProcessArgsFlags(ev *model.Event, process *model.Process) (flags []string) { +func (fh *EBPFFieldHandlers) ResolveProcessArgsFlags(ev *model.Event, process *model.Process) (flags []string) { return args.ParseProcessFlags(fh.ResolveProcessArgv(ev, process)) } // ResolveProcessArgsOptions resolves the arguments options of the event -func (fh *FieldHandlers) ResolveProcessArgsOptions(ev *model.Event, process *model.Process) (options []string) { +func (fh *EBPFFieldHandlers) ResolveProcessArgsOptions(ev *model.Event, process *model.Process) (options []string) { return args.ParseProcessOptions(fh.ResolveProcessArgv(ev, process)) } // ResolveFileFieldsInUpperLayer resolves whether the file is in an upper layer -func (fh *FieldHandlers) ResolveFileFieldsInUpperLayer(ev *model.Event, f *model.FileFields) bool { +func (fh *EBPFFieldHandlers) ResolveFileFieldsInUpperLayer(_ *model.Event, f *model.FileFields) bool { return f.GetInUpperLayer() } // ResolveXAttrName returns the string representation of the extended attribute name -func (fh *FieldHandlers) ResolveXAttrName(ev *model.Event, e *model.SetXAttrEvent) string { +func (fh *EBPFFieldHandlers) ResolveXAttrName(_ *model.Event, e *model.SetXAttrEvent) string { if len(e.Name) == 0 { e.Name, _ = model.UnmarshalString(e.NameRaw[:], 200) } @@ -101,7 +109,7 @@ func (fh *FieldHandlers) ResolveXAttrName(ev *model.Event, e *model.SetXAttrEven } // ResolveXAttrNamespace returns the string representation of the extended attribute namespace -func (fh *FieldHandlers) ResolveXAttrNamespace(ev *model.Event, e *model.SetXAttrEvent) string { +func (fh *EBPFFieldHandlers) ResolveXAttrNamespace(ev *model.Event, e *model.SetXAttrEvent) string { if len(e.Namespace) == 0 { ns, _, found := strings.Cut(fh.ResolveXAttrName(ev, e), ".") if found { @@ -112,7 +120,7 @@ func (fh *FieldHandlers) ResolveXAttrNamespace(ev *model.Event, e *model.SetXAtt } // ResolveMountPointPath resolves a mount point path -func (fh *FieldHandlers) ResolveMountPointPath(ev *model.Event, e *model.MountEvent) string { +func (fh *EBPFFieldHandlers) ResolveMountPointPath(ev *model.Event, e *model.MountEvent) string { if len(e.MountPointPath) == 0 { mountPointPath, err := fh.resolvers.MountResolver.ResolveMountPath(e.MountID, 0, ev.PIDContext.Pid, ev.ContainerContext.ID) if err != nil { @@ -125,7 +133,7 @@ func (fh *FieldHandlers) ResolveMountPointPath(ev *model.Event, e *model.MountEv } // ResolveMountSourcePath resolves a mount source path -func (fh *FieldHandlers) ResolveMountSourcePath(ev *model.Event, e *model.MountEvent) string { +func (fh *EBPFFieldHandlers) ResolveMountSourcePath(ev *model.Event, e *model.MountEvent) string { if e.BindSrcMountID != 0 && len(e.MountSourcePath) == 0 { bindSourceMountPath, err := fh.resolvers.MountResolver.ResolveMountPath(e.BindSrcMountID, 0, ev.PIDContext.Pid, ev.ContainerContext.ID) if err != nil { @@ -142,8 +150,21 @@ func (fh *FieldHandlers) ResolveMountSourcePath(ev *model.Event, e *model.MountE return e.MountSourcePath } +// ResolveMountRootPath resolves a mount root path +func (fh *EBPFFieldHandlers) ResolveMountRootPath(ev *model.Event, e *model.MountEvent) string { + if len(e.MountRootPath) == 0 { + mountRootPath, err := fh.resolvers.MountResolver.ResolveMountRoot(e.MountID, 0, ev.PIDContext.Pid, ev.ContainerContext.ID) + if err != nil { + e.MountRootPathResolutionError = err + return "" + } + e.MountRootPath = mountRootPath + } + return e.MountRootPath +} + // ResolveContainerContext queries the cgroup resolver to retrieve the ContainerContext of the event -func (fh *FieldHandlers) ResolveContainerContext(ev *model.Event) (*model.ContainerContext, bool) { +func (fh *EBPFFieldHandlers) ResolveContainerContext(ev *model.Event) (*model.ContainerContext, bool) { if ev.ContainerContext.ID != "" && !ev.ContainerContext.Resolved { if containerContext, _ := fh.resolvers.CGroupResolver.GetWorkload(ev.ContainerContext.ID); containerContext != nil { ev.ContainerContext = &containerContext.ContainerContext @@ -154,12 +175,12 @@ func (fh *FieldHandlers) ResolveContainerContext(ev *model.Event) (*model.Contai } // ResolveRights resolves the rights of a file -func (fh *FieldHandlers) ResolveRights(ev *model.Event, e *model.FileFields) int { +func (fh *EBPFFieldHandlers) ResolveRights(_ *model.Event, e *model.FileFields) int { return int(e.Mode) & (syscall.S_ISUID | syscall.S_ISGID | syscall.S_ISVTX | syscall.S_IRWXU | syscall.S_IRWXG | syscall.S_IRWXO) } // ResolveChownUID resolves the ResolveProcessCacheEntry id of a chown event to a username -func (fh *FieldHandlers) ResolveChownUID(ev *model.Event, e *model.ChownEvent) string { +func (fh *EBPFFieldHandlers) ResolveChownUID(ev *model.Event, e *model.ChownEvent) string { if len(e.User) == 0 { e.User, _ = fh.resolvers.UserGroupResolver.ResolveUser(int(e.UID), ev.ContainerContext.ID) } @@ -167,7 +188,7 @@ func (fh *FieldHandlers) ResolveChownUID(ev *model.Event, e *model.ChownEvent) s } // ResolveChownGID resolves the group id of a chown event to a group name -func (fh *FieldHandlers) ResolveChownGID(ev *model.Event, e *model.ChownEvent) string { +func (fh *EBPFFieldHandlers) ResolveChownGID(ev *model.Event, e *model.ChownEvent) string { if len(e.Group) == 0 { e.Group, _ = fh.resolvers.UserGroupResolver.ResolveGroup(int(e.GID), ev.ContainerContext.ID) } @@ -175,54 +196,59 @@ func (fh *FieldHandlers) ResolveChownGID(ev *model.Event, e *model.ChownEvent) s } // ResolveProcessArgv0 resolves the first arg of the event -func (fh *FieldHandlers) ResolveProcessArgv0(ev *model.Event, process *model.Process) string { +func (fh *EBPFFieldHandlers) ResolveProcessArgv0(_ *model.Event, process *model.Process) string { arg0, _ := sprocess.GetProcessArgv0(process) return arg0 } // ResolveProcessArgs resolves the args of the event -func (fh *FieldHandlers) ResolveProcessArgs(ev *model.Event, process *model.Process) string { +func (fh *EBPFFieldHandlers) ResolveProcessArgs(ev *model.Event, process *model.Process) string { return strings.Join(fh.ResolveProcessArgv(ev, process), " ") } +// ResolveProcessArgsScrubbed resolves the args of the event +func (fh *EBPFFieldHandlers) ResolveProcessArgsScrubbed(ev *model.Event, process *model.Process) string { + return strings.Join(fh.ResolveProcessArgvScrubbed(ev, process), " ") +} + // ResolveProcessArgv resolves the unscrubbed args of the process as an array. Use with caution. -func (fh *FieldHandlers) ResolveProcessArgv(ev *model.Event, process *model.Process) []string { +func (fh *EBPFFieldHandlers) ResolveProcessArgv(_ *model.Event, process *model.Process) []string { argv, _ := sprocess.GetProcessArgv(process) return argv } // ResolveProcessArgvScrubbed resolves the args of the process as an array -func (fh *FieldHandlers) ResolveProcessArgvScrubbed(ev *model.Event, process *model.Process) []string { //nolint:revive // TODO fix revive unused-parameter +func (fh *EBPFFieldHandlers) ResolveProcessArgvScrubbed(_ *model.Event, process *model.Process) []string { argv, _ := fh.resolvers.ProcessResolver.GetProcessArgvScrubbed(process) return argv } // ResolveProcessEnvp resolves the envp of the event as an array -func (fh *FieldHandlers) ResolveProcessEnvp(ev *model.Event, process *model.Process) []string { +func (fh *EBPFFieldHandlers) ResolveProcessEnvp(_ *model.Event, process *model.Process) []string { envp, _ := fh.resolvers.ProcessResolver.GetProcessEnvp(process) return envp } // ResolveProcessArgsTruncated returns whether the args are truncated -func (fh *FieldHandlers) ResolveProcessArgsTruncated(ev *model.Event, process *model.Process) bool { +func (fh *EBPFFieldHandlers) ResolveProcessArgsTruncated(_ *model.Event, process *model.Process) bool { _, truncated := sprocess.GetProcessArgv(process) return truncated } // ResolveProcessEnvsTruncated returns whether the envs are truncated -func (fh *FieldHandlers) ResolveProcessEnvsTruncated(ev *model.Event, process *model.Process) bool { +func (fh *EBPFFieldHandlers) ResolveProcessEnvsTruncated(_ *model.Event, process *model.Process) bool { _, truncated := fh.resolvers.ProcessResolver.GetProcessEnvs(process) return truncated } // ResolveProcessEnvs resolves the unscrubbed envs of the event. Use with caution. -func (fh *FieldHandlers) ResolveProcessEnvs(ev *model.Event, process *model.Process) []string { +func (fh *EBPFFieldHandlers) ResolveProcessEnvs(_ *model.Event, process *model.Process) []string { envs, _ := fh.resolvers.ProcessResolver.GetProcessEnvs(process) return envs } // ResolveSetuidUser resolves the user of the Setuid event -func (fh *FieldHandlers) ResolveSetuidUser(ev *model.Event, e *model.SetuidEvent) string { +func (fh *EBPFFieldHandlers) ResolveSetuidUser(ev *model.Event, e *model.SetuidEvent) string { if len(e.User) == 0 { e.User, _ = fh.resolvers.UserGroupResolver.ResolveUser(int(e.UID), ev.ContainerContext.ID) } @@ -230,7 +256,7 @@ func (fh *FieldHandlers) ResolveSetuidUser(ev *model.Event, e *model.SetuidEvent } // ResolveSetuidEUser resolves the effective user of the Setuid event -func (fh *FieldHandlers) ResolveSetuidEUser(ev *model.Event, e *model.SetuidEvent) string { +func (fh *EBPFFieldHandlers) ResolveSetuidEUser(ev *model.Event, e *model.SetuidEvent) string { if len(e.EUser) == 0 { e.EUser, _ = fh.resolvers.UserGroupResolver.ResolveUser(int(e.EUID), ev.ContainerContext.ID) } @@ -238,7 +264,7 @@ func (fh *FieldHandlers) ResolveSetuidEUser(ev *model.Event, e *model.SetuidEven } // ResolveSetuidFSUser resolves the file-system user of the Setuid event -func (fh *FieldHandlers) ResolveSetuidFSUser(ev *model.Event, e *model.SetuidEvent) string { +func (fh *EBPFFieldHandlers) ResolveSetuidFSUser(ev *model.Event, e *model.SetuidEvent) string { if len(e.FSUser) == 0 { e.FSUser, _ = fh.resolvers.UserGroupResolver.ResolveUser(int(e.FSUID), ev.ContainerContext.ID) } @@ -246,7 +272,7 @@ func (fh *FieldHandlers) ResolveSetuidFSUser(ev *model.Event, e *model.SetuidEve } // ResolveSetgidGroup resolves the group of the Setgid event -func (fh *FieldHandlers) ResolveSetgidGroup(ev *model.Event, e *model.SetgidEvent) string { +func (fh *EBPFFieldHandlers) ResolveSetgidGroup(ev *model.Event, e *model.SetgidEvent) string { if len(e.Group) == 0 { e.Group, _ = fh.resolvers.UserGroupResolver.ResolveUser(int(e.GID), ev.ContainerContext.ID) } @@ -254,7 +280,7 @@ func (fh *FieldHandlers) ResolveSetgidGroup(ev *model.Event, e *model.SetgidEven } // ResolveSetgidEGroup resolves the effective group of the Setgid event -func (fh *FieldHandlers) ResolveSetgidEGroup(ev *model.Event, e *model.SetgidEvent) string { +func (fh *EBPFFieldHandlers) ResolveSetgidEGroup(ev *model.Event, e *model.SetgidEvent) string { if len(e.EGroup) == 0 { e.EGroup, _ = fh.resolvers.UserGroupResolver.ResolveUser(int(e.EGID), ev.ContainerContext.ID) } @@ -262,7 +288,7 @@ func (fh *FieldHandlers) ResolveSetgidEGroup(ev *model.Event, e *model.SetgidEve } // ResolveSetgidFSGroup resolves the file-system group of the Setgid event -func (fh *FieldHandlers) ResolveSetgidFSGroup(ev *model.Event, e *model.SetgidEvent) string { +func (fh *EBPFFieldHandlers) ResolveSetgidFSGroup(ev *model.Event, e *model.SetgidEvent) string { if len(e.FSGroup) == 0 { e.FSGroup, _ = fh.resolvers.UserGroupResolver.ResolveUser(int(e.FSGID), ev.ContainerContext.ID) } @@ -270,7 +296,7 @@ func (fh *FieldHandlers) ResolveSetgidFSGroup(ev *model.Event, e *model.SetgidEv } // ResolveSELinuxBoolName resolves the boolean name of the SELinux event -func (fh *FieldHandlers) ResolveSELinuxBoolName(ev *model.Event, e *model.SELinuxEvent) string { +func (fh *EBPFFieldHandlers) ResolveSELinuxBoolName(_ *model.Event, e *model.SELinuxEvent) string { if e.EventKind != model.SELinuxBoolChangeEventKind { return "" } @@ -282,7 +308,7 @@ func (fh *FieldHandlers) ResolveSELinuxBoolName(ev *model.Event, e *model.SELinu } // GetProcessCacheEntry queries the ProcessResolver to retrieve the ProcessContext of the event -func (fh *FieldHandlers) GetProcessCacheEntry(ev *model.Event) (*model.ProcessCacheEntry, bool) { +func (fh *EBPFFieldHandlers) GetProcessCacheEntry(ev *model.Event) (*model.ProcessCacheEntry, bool) { ev.ProcessCacheEntry = fh.resolvers.ProcessResolver.Resolve(ev.PIDContext.Pid, ev.PIDContext.Tid, ev.PIDContext.ExecInode, false) if ev.ProcessCacheEntry == nil { ev.ProcessCacheEntry = model.GetPlaceholderProcessCacheEntry(ev.PIDContext.Pid, ev.PIDContext.Tid, false) @@ -292,7 +318,7 @@ func (fh *FieldHandlers) GetProcessCacheEntry(ev *model.Event) (*model.ProcessCa } // ResolveFileFieldsGroup resolves the group id of the file to a group name -func (fh *FieldHandlers) ResolveFileFieldsGroup(ev *model.Event, e *model.FileFields) string { +func (fh *EBPFFieldHandlers) ResolveFileFieldsGroup(ev *model.Event, e *model.FileFields) string { if len(e.Group) == 0 { e.Group, _ = fh.resolvers.UserGroupResolver.ResolveGroup(int(e.GID), ev.ContainerContext.ID) } @@ -300,7 +326,7 @@ func (fh *FieldHandlers) ResolveFileFieldsGroup(ev *model.Event, e *model.FileFi } // ResolveNetworkDeviceIfName returns the network iterface name from the network context -func (fh *FieldHandlers) ResolveNetworkDeviceIfName(ev *model.Event, device *model.NetworkDeviceContext) string { +func (fh *EBPFFieldHandlers) ResolveNetworkDeviceIfName(_ *model.Event, device *model.NetworkDeviceContext) string { if len(device.IfName) == 0 && fh.resolvers.TCResolver != nil { ifName, ok := fh.resolvers.TCResolver.ResolveNetworkDeviceIfName(device.IfIndex, device.NetNS) if ok { @@ -312,17 +338,31 @@ func (fh *FieldHandlers) ResolveNetworkDeviceIfName(ev *model.Event, device *mod } // ResolveFileFieldsUser resolves the user id of the file to a username -func (fh *FieldHandlers) ResolveFileFieldsUser(ev *model.Event, e *model.FileFields) string { +func (fh *EBPFFieldHandlers) ResolveFileFieldsUser(ev *model.Event, e *model.FileFields) string { if len(e.User) == 0 { e.User, _ = fh.resolvers.UserGroupResolver.ResolveUser(int(e.UID), ev.ContainerContext.ID) } return e.User } +// ResolveEventTimestamp resolves the monolitic kernel event timestamp to an absolute time +func (fh *EBPFFieldHandlers) ResolveEventTimestamp(ev *model.Event, e *model.BaseEvent) int { + return int(fh.ResolveEventTime(ev, e).UnixNano()) +} + +// GetProcessService returns the service tag based on the process context +func (fh *EBPFFieldHandlers) GetProcessService(ev *model.Event) string { + entry, _ := fh.ResolveProcessCacheEntry(ev) + if entry == nil { + return "" + } + return getProcessService(entry) +} + // ResolveEventTime resolves the monolitic kernel event timestamp to an absolute time -func (fh *FieldHandlers) ResolveEventTime(ev *model.Event) time.Time { +func (fh *EBPFFieldHandlers) ResolveEventTime(ev *model.Event, _ *model.BaseEvent) time.Time { if ev.Timestamp.IsZero() { - fh := ev.FieldHandlers.(*FieldHandlers) + fh := ev.FieldHandlers.(*EBPFFieldHandlers) ev.Timestamp = fh.resolvers.TimeResolver.ResolveMonotonicTimestamp(ev.TimestampRaw) if ev.Timestamp.IsZero() { @@ -333,13 +373,13 @@ func (fh *FieldHandlers) ResolveEventTime(ev *model.Event) time.Time { } // ResolveAsync resolves the async flag -func (fh *FieldHandlers) ResolveAsync(ev *model.Event) bool { +func (fh *EBPFFieldHandlers) ResolveAsync(ev *model.Event) bool { ev.Async = ev.Flags&model.EventFlagsAsync > 0 return ev.Async } // ResolvePackageName resolves the name of the package providing this file -func (fh *FieldHandlers) ResolvePackageName(ev *model.Event, f *model.FileEvent) string { +func (fh *EBPFFieldHandlers) ResolvePackageName(ev *model.Event, f *model.FileEvent) string { if f.PkgName == "" { // Force the resolution of file path to be able to map to a package provided file if fh.ResolveFilePath(ev, f) == "" { @@ -358,7 +398,7 @@ func (fh *FieldHandlers) ResolvePackageName(ev *model.Event, f *model.FileEvent) } // ResolvePackageVersion resolves the version of the package providing this file -func (fh *FieldHandlers) ResolvePackageVersion(ev *model.Event, f *model.FileEvent) string { +func (fh *EBPFFieldHandlers) ResolvePackageVersion(ev *model.Event, f *model.FileEvent) string { if f.PkgVersion == "" { // Force the resolution of file path to be able to map to a package provided file if fh.ResolveFilePath(ev, f) == "" { @@ -377,7 +417,7 @@ func (fh *FieldHandlers) ResolvePackageVersion(ev *model.Event, f *model.FileEve } // ResolvePackageSourceVersion resolves the version of the source package of the package providing this file -func (fh *FieldHandlers) ResolvePackageSourceVersion(ev *model.Event, f *model.FileEvent) string { +func (fh *EBPFFieldHandlers) ResolvePackageSourceVersion(ev *model.Event, f *model.FileEvent) string { if f.PkgSrcVersion == "" { // Force the resolution of file path to be able to map to a package provided file if fh.ResolveFilePath(ev, f) == "" { @@ -396,7 +436,7 @@ func (fh *FieldHandlers) ResolvePackageSourceVersion(ev *model.Event, f *model.F } // ResolveModuleArgv resolves the unscrubbed args of the module as an array. Use with caution. -func (fh *FieldHandlers) ResolveModuleArgv(ev *model.Event, module *model.LoadModuleEvent) []string { +func (fh *EBPFFieldHandlers) ResolveModuleArgv(_ *model.Event, module *model.LoadModuleEvent) []string { // strings.Split return [""] if args is empty, so we do a manual check before if len(module.Args) == 0 { module.Argv = nil @@ -411,7 +451,7 @@ func (fh *FieldHandlers) ResolveModuleArgv(ev *model.Event, module *model.LoadMo } // ResolveModuleArgs resolves the correct args if the arguments were truncated, if not return module.Args -func (fh *FieldHandlers) ResolveModuleArgs(ev *model.Event, module *model.LoadModuleEvent) string { +func (fh *EBPFFieldHandlers) ResolveModuleArgs(_ *model.Event, module *model.LoadModuleEvent) string { if module.ArgsTruncated { argsTmp := strings.Split(module.Args, " ") argsTmp = argsTmp[:len(argsTmp)-1] @@ -421,11 +461,84 @@ func (fh *FieldHandlers) ResolveModuleArgs(ev *model.Event, module *model.LoadMo } // ResolveHashesFromEvent resolves the hashes of the requested event -func (fh *FieldHandlers) ResolveHashesFromEvent(ev *model.Event, f *model.FileEvent) []string { +func (fh *EBPFFieldHandlers) ResolveHashesFromEvent(ev *model.Event, f *model.FileEvent) []string { return fh.resolvers.HashResolver.ComputeHashesFromEvent(ev, f) } // ResolveHashes resolves the hashes of the requested file event -func (fh *FieldHandlers) ResolveHashes(eventType model.EventType, process *model.Process, file *model.FileEvent) []string { +func (fh *EBPFFieldHandlers) ResolveHashes(eventType model.EventType, process *model.Process, file *model.FileEvent) []string { return fh.resolvers.HashResolver.ComputeHashes(eventType, process, file) } + +// ResolveContainerID resolves the container ID of the event +func (fh *EBPFFieldHandlers) ResolveContainerID(ev *model.Event, e *model.ContainerContext) string { + if len(e.ID) == 0 { + if entry, _ := fh.ResolveProcessCacheEntry(ev); entry != nil { + e.ID = entry.ContainerID + } + } + return e.ID +} + +// ResolveContainerCreatedAt resolves the container creation time of the event +func (fh *EBPFFieldHandlers) ResolveContainerCreatedAt(ev *model.Event, e *model.ContainerContext) int { + if e.CreatedAt == 0 { + if containerContext, _ := fh.ResolveContainerContext(ev); containerContext != nil { + e.CreatedAt = containerContext.CreatedAt + } + } + return int(e.CreatedAt) +} + +// ResolveContainerTags resolves the container tags of the event +func (fh *EBPFFieldHandlers) ResolveContainerTags(_ *model.Event, e *model.ContainerContext) []string { + if len(e.Tags) == 0 && e.ID != "" { + e.Tags = fh.resolvers.TagsResolver.Resolve(e.ID) + } + return e.Tags +} + +// ResolveProcessCreatedAt resolves process creation time +func (fh *EBPFFieldHandlers) ResolveProcessCreatedAt(_ *model.Event, e *model.Process) int { + return int(e.ExecTime.UnixNano()) +} + +// ResolveK8SUsername resolves the k8s username of the event +func (fh *EBPFFieldHandlers) ResolveK8SUsername(_ *model.Event, evtCtx *model.UserSessionContext) string { + if !evtCtx.Resolved { + if ctx := fh.resolvers.UserSessions.ResolveUserSession(evtCtx.ID); ctx != nil { + *evtCtx = *ctx + } + } + return evtCtx.K8SUsername +} + +// ResolveK8SUID resolves the k8s UID of the event +func (fh *EBPFFieldHandlers) ResolveK8SUID(_ *model.Event, evtCtx *model.UserSessionContext) string { + if !evtCtx.Resolved { + if ctx := fh.resolvers.UserSessions.ResolveUserSession(evtCtx.ID); ctx != nil { + *evtCtx = *ctx + } + } + return evtCtx.K8SUID +} + +// ResolveK8SGroups resolves the k8s groups of the event +func (fh *EBPFFieldHandlers) ResolveK8SGroups(_ *model.Event, evtCtx *model.UserSessionContext) []string { + if !evtCtx.Resolved { + if ctx := fh.resolvers.UserSessions.ResolveUserSession(evtCtx.ID); ctx != nil { + *evtCtx = *ctx + } + } + return evtCtx.K8SGroups +} + +// ResolveK8SExtra resolves the k8s extra of the event +func (fh *EBPFFieldHandlers) ResolveK8SExtra(_ *model.Event, evtCtx *model.UserSessionContext) map[string][]string { + if !evtCtx.Resolved { + if ctx := fh.resolvers.UserSessions.ResolveUserSession(evtCtx.ID); ctx != nil { + *evtCtx = *ctx + } + } + return evtCtx.K8SExtra +} diff --git a/pkg/security/probe/field_handlers_ebpfless.go b/pkg/security/probe/field_handlers_ebpfless.go new file mode 100644 index 0000000000000..2b873788152c5 --- /dev/null +++ b/pkg/security/probe/field_handlers_ebpfless.go @@ -0,0 +1,147 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux + +// Package probe holds probe related files +package probe + +import ( + "strings" + "time" + + "github.com/DataDog/datadog-agent/pkg/security/resolvers" + sprocess "github.com/DataDog/datadog-agent/pkg/security/resolvers/process" + + "github.com/DataDog/datadog-agent/pkg/security/secl/args" + "github.com/DataDog/datadog-agent/pkg/security/secl/model" +) + +// EBPFLessFieldHandlers defines a field handlers +type EBPFLessFieldHandlers struct { + // TODO(safchain) remove this when support for multiple platform with the same build tags is available + // keeping it can be dangerous as it can hide non implemented handlers + model.DefaultFieldHandlers + + resolvers *resolvers.EBPFLessResolvers +} + +// GetProcessService returns the service tag based on the process context +func (fh *EBPFLessFieldHandlers) GetProcessService(ev *model.Event) string { + entry, _ := fh.ResolveProcessCacheEntry(ev) + if entry == nil { + return "" + } + return getProcessService(entry) +} + +// ResolveProcessCacheEntry queries the ProcessResolver to retrieve the ProcessContext of the event +func (fh *EBPFLessFieldHandlers) ResolveProcessCacheEntry(ev *model.Event) (*model.ProcessCacheEntry, bool) { + if ev.ProcessCacheEntry == nil && ev.PIDContext.Pid != 0 { + ev.ProcessCacheEntry = fh.resolvers.ProcessResolver.Resolve(ev.PIDContext.Pid) + } + + if ev.ProcessCacheEntry == nil { + ev.ProcessCacheEntry = model.GetPlaceholderProcessCacheEntry(ev.PIDContext.Pid, ev.PIDContext.Pid, false) + return ev.ProcessCacheEntry, false + } + + return ev.ProcessCacheEntry, true +} + +// ResolveFilePath resolves the inode to a full path +func (fh *EBPFLessFieldHandlers) ResolveFilePath(_ *model.Event, f *model.FileEvent) string { + return f.PathnameStr +} + +// ResolveFileBasename resolves the inode to a full path +func (fh *EBPFLessFieldHandlers) ResolveFileBasename(_ *model.Event, f *model.FileEvent) string { + return f.BasenameStr +} + +// ResolveProcessArgsFlags resolves the arguments flags of the event +func (fh *EBPFLessFieldHandlers) ResolveProcessArgsFlags(ev *model.Event, process *model.Process) (flags []string) { + return args.ParseProcessFlags(fh.ResolveProcessArgv(ev, process)) +} + +// ResolveProcessArgsOptions resolves the arguments options of the event +func (fh *EBPFLessFieldHandlers) ResolveProcessArgsOptions(ev *model.Event, process *model.Process) (options []string) { + return args.ParseProcessOptions(fh.ResolveProcessArgv(ev, process)) +} + +// ResolveContainerContext retrieve the ContainerContext of the event +func (fh *EBPFLessFieldHandlers) ResolveContainerContext(ev *model.Event) (*model.ContainerContext, bool) { + return ev.ContainerContext, ev.ContainerContext != nil +} + +// ResolveProcessArgv0 resolves the first arg of the event +func (fh *EBPFLessFieldHandlers) ResolveProcessArgv0(_ *model.Event, process *model.Process) string { + arg0, _ := sprocess.GetProcessArgv0(process) + return arg0 +} + +// ResolveProcessArgs resolves the args of the event +func (fh *EBPFLessFieldHandlers) ResolveProcessArgs(ev *model.Event, process *model.Process) string { + return strings.Join(fh.ResolveProcessArgv(ev, process), " ") +} + +// ResolveProcessArgv resolves the unscrubbed args of the process as an array. Use with caution. +func (fh *EBPFLessFieldHandlers) ResolveProcessArgv(_ *model.Event, process *model.Process) []string { + argv, _ := sprocess.GetProcessArgv(process) + return argv +} + +// ResolveProcessArgvScrubbed resolves the args of the process as an array +func (fh *EBPFLessFieldHandlers) ResolveProcessArgvScrubbed(_ *model.Event, process *model.Process) []string { + argv, _ := fh.resolvers.ProcessResolver.GetProcessArgvScrubbed(process) + return argv +} + +// ResolveProcessArgsScrubbed resolves the args of the event +func (fh *EBPFLessFieldHandlers) ResolveProcessArgsScrubbed(ev *model.Event, process *model.Process) string { + return strings.Join(fh.ResolveProcessArgvScrubbed(ev, process), " ") +} + +// ResolveProcessEnvp resolves the envp of the event as an array +func (fh *EBPFLessFieldHandlers) ResolveProcessEnvp(_ *model.Event, process *model.Process) []string { + envp, _ := fh.resolvers.ProcessResolver.GetProcessEnvp(process) + return envp +} + +// ResolveProcessArgsTruncated returns whether the args are truncated +func (fh *EBPFLessFieldHandlers) ResolveProcessArgsTruncated(_ *model.Event, process *model.Process) bool { + _, truncated := sprocess.GetProcessArgv(process) + return truncated +} + +// ResolveProcessEnvsTruncated returns whether the envs are truncated +func (fh *EBPFLessFieldHandlers) ResolveProcessEnvsTruncated(_ *model.Event, process *model.Process) bool { + _, truncated := fh.resolvers.ProcessResolver.GetProcessEnvs(process) + return truncated +} + +// ResolveProcessEnvs resolves the unscrubbed envs of the event. Use with caution. +func (fh *EBPFLessFieldHandlers) ResolveProcessEnvs(_ *model.Event, process *model.Process) []string { + envs, _ := fh.resolvers.ProcessResolver.GetProcessEnvs(process) + return envs +} + +// GetProcessCacheEntry queries the ProcessResolver to retrieve the ProcessContext of the event +func (fh *EBPFLessFieldHandlers) GetProcessCacheEntry(ev *model.Event) (*model.ProcessCacheEntry, bool) { + ev.ProcessCacheEntry = fh.resolvers.ProcessResolver.Resolve(ev.PIDContext.Pid) + if ev.ProcessCacheEntry == nil { + ev.ProcessCacheEntry = model.GetPlaceholderProcessCacheEntry(ev.PIDContext.Pid, ev.PIDContext.Pid, false) + return ev.ProcessCacheEntry, false + } + return ev.ProcessCacheEntry, true +} + +// ResolveEventTime resolves the monolitic kernel event timestamp to an absolute time +func (fh *EBPFLessFieldHandlers) ResolveEventTime(ev *model.Event, _ *model.BaseEvent) time.Time { + if ev.Timestamp.IsZero() { + ev.Timestamp = time.Now() + } + return ev.Timestamp +} diff --git a/pkg/security/probe/field_handlers_windows.go b/pkg/security/probe/field_handlers_windows.go index 427c401a9cd0f..d3ad4ee796307 100644 --- a/pkg/security/probe/field_handlers_windows.go +++ b/pkg/security/probe/field_handlers_windows.go @@ -9,11 +9,21 @@ package probe import ( "time" + "github.com/DataDog/datadog-agent/pkg/security/resolvers" "github.com/DataDog/datadog-agent/pkg/security/secl/model" ) +// FieldHandlers defines a field handlers +type FieldHandlers struct { + // TODO(safchain) remove this when support for multiple platform with the same build tags is available + // keeping it can be dangerous as it can hide non implemented handlers + model.DefaultFieldHandlers + + resolvers *resolvers.Resolvers +} + // ResolveEventTime resolves the monolitic kernel event timestamp to an absolute time -func (fh *FieldHandlers) ResolveEventTime(ev *model.Event) time.Time { +func (fh *FieldHandlers) ResolveEventTime(ev *model.Event, _ *model.BaseEvent) time.Time { if ev.Timestamp.IsZero() { ev.Timestamp = time.Now() } @@ -58,3 +68,12 @@ func (fh *FieldHandlers) ResolveProcessCacheEntry(ev *model.Event) (*model.Proce return ev.ProcessCacheEntry, true } + +// GetProcessService returns the service tag based on the process context +func (fh *FieldHandlers) GetProcessService(ev *model.Event) string { + entry, _ := fh.ResolveProcessCacheEntry(ev) + if entry == nil { + return "" + } + return getProcessService(entry) +} diff --git a/pkg/security/probe/model.go b/pkg/security/probe/model.go index 44aa73f9bc65c..aa70301434fcd 100644 --- a/pkg/security/probe/model.go +++ b/pkg/security/probe/model.go @@ -8,18 +8,7 @@ // Package probe holds probe related files package probe -import ( - "github.com/DataDog/datadog-agent/pkg/security/secl/model" -) - const ( // ServiceEnvVar environment variable used to report service ServiceEnvVar = "DD_SERVICE" ) - -// NewEvent returns a new event -func NewEvent(fh *FieldHandlers) *model.Event { - event := model.NewDefaultEvent() - event.FieldHandlers = fh - return event -} diff --git a/pkg/security/probe/model_linux.go b/pkg/security/probe/model_ebpf.go similarity index 79% rename from pkg/security/probe/model_linux.go rename to pkg/security/probe/model_ebpf.go index f353a6c7da21f..4ead826c0a544 100644 --- a/pkg/security/probe/model_linux.go +++ b/pkg/security/probe/model_ebpf.go @@ -3,6 +3,8 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. +//go:build linux + // Package probe holds probe related files package probe @@ -14,8 +16,8 @@ import ( "github.com/DataDog/datadog-agent/pkg/security/secl/model" ) -// NewModel returns a new model with some extra field validation -func NewModel(probe *Probe) *model.Model { +// NewEBPFModel returns a new model with some extra field validation +func NewEBPFModel(probe *EBPFProbe) *model.Model { return &model.Model{ ExtraValidateFieldFnc: func(field eval.Field, fieldValue eval.FieldValue) error { switch field { @@ -34,3 +36,10 @@ func NewModel(probe *Probe) *model.Model { }, } } + +// NewEBPFEvent returns a new event +func NewEBPFEvent(fh *EBPFFieldHandlers) *model.Event { + event := model.NewDefaultEvent() + event.FieldHandlers = fh + return event +} diff --git a/pkg/security/probe/model_ebpfless.go b/pkg/security/probe/model_ebpfless.go new file mode 100644 index 0000000000000..9a13f7301acfb --- /dev/null +++ b/pkg/security/probe/model_ebpfless.go @@ -0,0 +1,41 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux + +// Package probe holds probe related files +package probe + +import ( + "fmt" + "strings" + + "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" + "github.com/DataDog/datadog-agent/pkg/security/secl/model" +) + +// NewEBPFLessModel returns a new model with some extra field validation +func NewEBPFLessModel() *model.Model { + return &model.Model{ + ExtraValidateFieldFnc: func(field eval.Field, fieldValue eval.FieldValue) error { + // TODO(safchain) remove this check when multiple model per platform will be supported in the SECL package + if !strings.HasPrefix(field, "exec.") && + !strings.HasPrefix(field, "exit.") && + !strings.HasPrefix(field, "open.") && + !strings.HasPrefix(field, "process.") && + !strings.HasPrefix(field, "container.") { + return fmt.Errorf("%s is not available with the eBPF less version", field) + } + return nil + }, + } +} + +// NewEBPFLessEvent returns a new event +func NewEBPFLessEvent(fh *EBPFLessFieldHandlers) *model.Event { + event := model.NewDefaultEvent() + event.FieldHandlers = fh + return event +} diff --git a/pkg/security/probe/model_test.go b/pkg/security/probe/model_test.go index 36729e2390683..8530478a36a01 100644 --- a/pkg/security/probe/model_test.go +++ b/pkg/security/probe/model_test.go @@ -30,7 +30,7 @@ func TestProcessArgsFlags(t *testing.T) { "-9", "-", "--", } - resolver, _ := process.NewResolver(&manager.Manager{}, &config.Config{}, &statsd.NoOpClient{}, + resolver, _ := process.NewEBPFResolver(&manager.Manager{}, &config.Config{}, &statsd.NoOpClient{}, &procutil.DataScrubber{}, nil, nil, nil, nil, nil, nil, process.NewResolverOpts()) e := model.Event{ @@ -40,8 +40,8 @@ func TestProcessArgsFlags(t *testing.T) { }, }, BaseEvent: model.BaseEvent{ - FieldHandlers: &FieldHandlers{ - resolvers: &resolvers.Resolvers{ + FieldHandlers: &EBPFFieldHandlers{ + resolvers: &resolvers.EBPFResolvers{ ProcessResolver: resolver, }, }, @@ -93,7 +93,7 @@ func TestProcessArgsOptions(t *testing.T) { "--", "---", "-9", } - resolver, _ := process.NewResolver(&manager.Manager{}, &config.Config{}, &statsd.NoOpClient{}, + resolver, _ := process.NewEBPFResolver(&manager.Manager{}, &config.Config{}, &statsd.NoOpClient{}, &procutil.DataScrubber{}, nil, nil, nil, nil, nil, nil, process.NewResolverOpts()) e := model.Event{ @@ -103,8 +103,8 @@ func TestProcessArgsOptions(t *testing.T) { }, }, BaseEvent: model.BaseEvent{ - FieldHandlers: &FieldHandlers{ - resolvers: &resolvers.Resolvers{ + FieldHandlers: &EBPFFieldHandlers{ + resolvers: &resolvers.EBPFResolvers{ ProcessResolver: resolver, }, }, diff --git a/pkg/security/probe/model_windows.go b/pkg/security/probe/model_windows.go index 43c9ba728186a..cb7520cd64691 100644 --- a/pkg/security/probe/model_windows.go +++ b/pkg/security/probe/model_windows.go @@ -7,10 +7,31 @@ package probe import ( + "fmt" + "strings" + + "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" "github.com/DataDog/datadog-agent/pkg/security/secl/model" ) -// NewModel returns a new model with some extra field validation -func NewModel(probe *Probe) *model.Model { - return &model.Model{} +// NewWindowsModel returns a new model with some extra field validation +func NewWindowsModel(_ *WindowsProbe) *model.Model { + return &model.Model{ + ExtraValidateFieldFnc: func(field eval.Field, fieldValue eval.FieldValue) error { + // TODO(safchain) remove this check when multiple model per platform will be supported in the SECL package + if !strings.HasPrefix(field, "exec.") && + !strings.HasPrefix(field, "exit.") && + !strings.HasPrefix(field, "process.") { + return fmt.Errorf("%s is not available with the Windows version", field) + } + return nil + }, + } +} + +// NewWindowsEvent returns a new event +func NewWindowsEvent(fh *FieldHandlers) *model.Event { + event := model.NewDefaultEvent() + event.FieldHandlers = fh + return event } diff --git a/pkg/security/probe/opts_linux.go b/pkg/security/probe/opts_linux.go index cd9bb52f7c7fd..4126b42b11e86 100644 --- a/pkg/security/probe/opts_linux.go +++ b/pkg/security/probe/opts_linux.go @@ -27,6 +27,8 @@ type Opts struct { SyscallsMonitorEnabled bool // TTYFallbackEnabled enable the tty procfs fallback TTYFallbackEnabled bool + // EBPFLessEnabled use ebpfless source + EBPFLessEnabled bool } func (o *Opts) normalize() { diff --git a/pkg/security/probe/probe.go b/pkg/security/probe/probe.go index f4e6e91da5789..e5f29f320974e 100644 --- a/pkg/security/probe/probe.go +++ b/pkg/security/probe/probe.go @@ -9,24 +9,44 @@ package probe import ( - "context" "errors" - "sync" "time" "github.com/DataDog/datadog-go/v5/statsd" - "golang.org/x/time/rate" "github.com/DataDog/datadog-agent/pkg/process/procutil" "github.com/DataDog/datadog-agent/pkg/security/config" "github.com/DataDog/datadog-agent/pkg/security/events" - "github.com/DataDog/datadog-agent/pkg/security/resolvers" + "github.com/DataDog/datadog-agent/pkg/security/probe/kfilters" "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/secl/rules" "github.com/DataDog/datadog-agent/pkg/security/seclog" + "github.com/DataDog/datadog-agent/pkg/security/serializers" ) +// PlatformProbe defines a platform dependant probe +type PlatformProbe interface { + Setup() error + Init() error + Start() error + Stop() + SendStats() error + Snapshot() error + Close() error + NewModel() *model.Model + DumpDiscarders() (string, error) + FlushDiscarders() error + ApplyRuleSet(_ *rules.RuleSet) (*kfilters.ApplyRuleSetReport, error) + OnNewDiscarder(_ *rules.RuleSet, _ *model.Event, _ eval.Field, _ eval.EventType) + HandleActions(_ *rules.Rule, _ eval.Event) + NewEvent() *model.Event + GetFieldHandlers() model.FieldHandlers + DumpProcessCache(_ bool) (string, error) + AddDiscarderPushedCallback(_ DiscarderPushedCallback) + GetEventTags(_ string) []string +} + // FullAccessEventHandler represents a handler for events sent by the probe that needs access to all the fields in the SECL model type FullAccessEventHandler interface { HandleEvent(event *model.Event) @@ -43,41 +63,106 @@ type CustomEventHandler interface { HandleCustomEvent(rule *rules.Rule, event *events.CustomEvent) } -// NotifyDiscarderPushedCallback describe the callback used to retrieve pushed discarders information -type NotifyDiscarderPushedCallback func(eventType string, event *model.Event, field string) +// DiscarderPushedCallback describe the callback used to retrieve pushed discarders information +type DiscarderPushedCallback func(eventType string, event *model.Event, field string) // Probe represents the runtime security eBPF probe in charge of // setting up the required kProbes and decoding events sent from the kernel type Probe struct { - PlatformProbe + PlatformProbe PlatformProbe // Constants and configuration Opts Opts Config *config.Config StatsdClient statsd.ClientInterface startTime time.Time - ctx context.Context - cancelFnc context.CancelFunc - wg sync.WaitGroup // internals scrubber *procutil.DataScrubber + event *model.Event // Events section fullAccessEventHandlers [model.MaxAllEventType][]FullAccessEventHandler eventHandlers [model.MaxAllEventType][]EventHandler customEventHandlers [model.MaxAllEventType][]CustomEventHandler +} - discarderRateLimiter *rate.Limiter - // internals - resolvers *resolvers.Resolvers - fieldHandlers *FieldHandlers - event *model.Event +// Init initializes the probe +func (p *Probe) Init() error { + p.startTime = time.Now() + return p.PlatformProbe.Init() +} + +// Setup the runtime security probe +func (p *Probe) Setup() error { + return p.PlatformProbe.Setup() +} + +// Start plays the snapshot data and then start the event stream +func (p *Probe) Start() error { + return p.PlatformProbe.Start() +} + +// SendStats sends statistics about the probe to Datadog +func (p *Probe) SendStats() error { + return p.PlatformProbe.SendStats() +} + +// Close the probe +func (p *Probe) Close() error { + return p.PlatformProbe.Close() +} + +// Stop the probe +func (p *Probe) Stop() { + p.PlatformProbe.Stop() +} + +// FlushDiscarders invalidates all the discarders +func (p *Probe) FlushDiscarders() error { + seclog.Debugf("Flushing discarders") + return p.PlatformProbe.FlushDiscarders() +} + +// ApplyRuleSet setup the probes for the provided set of rules and returns the policy report. +func (p *Probe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.ApplyRuleSetReport, error) { + return p.PlatformProbe.ApplyRuleSet(rs) +} + +// Snapshot runs the different snapshot functions of the resolvers that +// require to sync with the current state of the system +func (p *Probe) Snapshot() error { + return p.PlatformProbe.Snapshot() +} + +// OnNewDiscarder is called when a new discarder is found +func (p *Probe) OnNewDiscarder(rs *rules.RuleSet, ev *model.Event, field eval.Field, eventType eval.EventType) { + p.PlatformProbe.OnNewDiscarder(rs, ev, field, eventType) +} + +// DumpDiscarders removes all the discarders +func (p *Probe) DumpDiscarders() (string, error) { + seclog.Debugf("Dumping discarders") + return p.PlatformProbe.DumpDiscarders() +} + +// DumpProcessCache dump the process cache +func (p *Probe) DumpProcessCache(withArgs bool) (string, error) { + return p.PlatformProbe.DumpProcessCache(withArgs) +} + +// GetDebugStats returns the debug stats +func (p *Probe) GetDebugStats() map[string]interface{} { + debug := map[string]interface{}{ + "start_time": p.startTime.String(), + } + // TODO(Will): add manager state + return debug } -// GetResolvers returns the resolvers of Probe -func (p *Probe) GetResolvers() *resolvers.Resolvers { - return p.resolvers +// HandleActions executes the actions of a triggered rule +func (p *Probe) HandleActions(rule *rules.Rule, event eval.Event) { + p.PlatformProbe.HandleActions(rule, event) } // AddEventHandler sets a probe event handler @@ -109,9 +194,58 @@ func (p *Probe) AddCustomEventHandler(eventType model.EventType, handler CustomE return nil } +func (p *Probe) sendEventToWildcardHandlers(event *model.Event) { + for _, handler := range p.fullAccessEventHandlers[model.UnknownEventType] { + handler.HandleEvent(event) + } +} + +func (p *Probe) sendEventToSpecificEventTypeHandlers(event *model.Event) { + for _, handler := range p.eventHandlers[event.GetEventType()] { + handler.HandleEvent(handler.Copy(event)) + } +} + +func traceEvent(fmt string, marshaller func() ([]byte, model.EventType, error)) { + if !seclog.DefaultLogger.IsTracing() { + return + } + + eventJSON, eventType, err := marshaller() + if err != nil { + seclog.DefaultLogger.TraceTagf(eventType, fmt, err) + return + } + + seclog.DefaultLogger.TraceTagf(eventType, fmt, string(eventJSON)) +} + +// AddDiscarderPushedCallback add a callback to the list of func that have to be called when a discarder is pushed to kernel +func (p *Probe) AddDiscarderPushedCallback(cb DiscarderPushedCallback) { + p.PlatformProbe.AddDiscarderPushedCallback(cb) +} + +// DispatchCustomEvent sends a custom event to the probe event handler +func (p *Probe) DispatchCustomEvent(rule *rules.Rule, event *events.CustomEvent) { + traceEvent("Dispatching custom event %s", func() ([]byte, model.EventType, error) { + eventJSON, err := serializers.MarshalCustomEvent(event) + return eventJSON, event.GetEventType(), err + }) + + // send wildcard first + for _, handler := range p.customEventHandlers[model.UnknownEventType] { + handler.HandleCustomEvent(rule, event) + } + + // send specific event + for _, handler := range p.customEventHandlers[event.GetEventType()] { + handler.HandleCustomEvent(rule, event) + } +} + func (p *Probe) zeroEvent() *model.Event { p.event.Zero() - p.event.FieldHandlers = p.fieldHandlers + p.event.FieldHandlers = p.PlatformProbe.GetFieldHandlers() return p.event } @@ -122,7 +256,7 @@ func (p *Probe) StatsPollingInterval() time.Duration { // GetEventTags returns the event tags func (p *Probe) GetEventTags(containerID string) []string { - return p.GetResolvers().TagsResolver.Resolve(containerID) + return p.PlatformProbe.GetEventTags(containerID) } // GetService returns the service name from the process tree @@ -146,10 +280,10 @@ func (p *Probe) NewEvaluationSet(eventTypeEnabled map[eval.EventType]bool, ruleS } eventCtor := func() eval.Event { - return NewEvent(p.fieldHandlers) + return p.PlatformProbe.NewEvent() } - rs := rules.NewRuleSet(NewModel(p), eventCtor, ruleOpts.WithRuleSetTag(ruleSetTagValue), evalOpts) + rs := rules.NewRuleSet(p.PlatformProbe.NewModel(), eventCtor, ruleOpts.WithRuleSetTag(ruleSetTagValue), evalOpts) ruleSetsToInclude = append(ruleSetsToInclude, rs) } diff --git a/pkg/security/probe/probe_ebpf.go b/pkg/security/probe/probe_ebpf.go new file mode 100644 index 0000000000000..5dace2eff2f81 --- /dev/null +++ b/pkg/security/probe/probe_ebpf.go @@ -0,0 +1,1924 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux + +// Package probe holds probe related files +package probe + +import ( + "context" + "errors" + "fmt" + "math" + "os" + "path/filepath" + "runtime" + "sync" + "syscall" + "time" + + lib "github.com/cilium/ebpf" + "github.com/hashicorp/go-multierror" + "github.com/moby/sys/mountinfo" + "golang.org/x/exp/slices" + "golang.org/x/sys/unix" + "golang.org/x/time/rate" + "gopkg.in/yaml.v3" + + "github.com/DataDog/datadog-go/v5/statsd" + manager "github.com/DataDog/ebpf-manager" + "github.com/DataDog/ebpf-manager/tracefs" + + "github.com/DataDog/datadog-agent/pkg/collector/corechecks/ebpf/probe/ebpfcheck" + aconfig "github.com/DataDog/datadog-agent/pkg/config" + commonebpf "github.com/DataDog/datadog-agent/pkg/ebpf" + "github.com/DataDog/datadog-agent/pkg/security/config" + "github.com/DataDog/datadog-agent/pkg/security/ebpf" + "github.com/DataDog/datadog-agent/pkg/security/ebpf/kernel" + "github.com/DataDog/datadog-agent/pkg/security/ebpf/probes" + "github.com/DataDog/datadog-agent/pkg/security/events" + "github.com/DataDog/datadog-agent/pkg/security/metrics" + pconfig "github.com/DataDog/datadog-agent/pkg/security/probe/config" + "github.com/DataDog/datadog-agent/pkg/security/probe/constantfetch" + "github.com/DataDog/datadog-agent/pkg/security/probe/erpc" + "github.com/DataDog/datadog-agent/pkg/security/probe/eventstream" + "github.com/DataDog/datadog-agent/pkg/security/probe/eventstream/reorderer" + "github.com/DataDog/datadog-agent/pkg/security/probe/eventstream/ringbuffer" + "github.com/DataDog/datadog-agent/pkg/security/probe/kfilters" + "github.com/DataDog/datadog-agent/pkg/security/probe/managerhelper" + "github.com/DataDog/datadog-agent/pkg/security/resolvers" + "github.com/DataDog/datadog-agent/pkg/security/resolvers/mount" + "github.com/DataDog/datadog-agent/pkg/security/resolvers/netns" + "github.com/DataDog/datadog-agent/pkg/security/resolvers/path" + "github.com/DataDog/datadog-agent/pkg/security/resolvers/process" + "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" + "github.com/DataDog/datadog-agent/pkg/security/secl/model" + "github.com/DataDog/datadog-agent/pkg/security/secl/rules" + "github.com/DataDog/datadog-agent/pkg/security/seclog" + "github.com/DataDog/datadog-agent/pkg/security/security_profile/dump" + "github.com/DataDog/datadog-agent/pkg/security/serializers" + "github.com/DataDog/datadog-agent/pkg/security/utils" + utilkernel "github.com/DataDog/datadog-agent/pkg/util/kernel" + "github.com/DataDog/datadog-agent/pkg/util/log" +) + +// EventStream describes the interface implemented by reordered perf maps or ring buffers +type EventStream interface { + Init(*manager.Manager, *pconfig.Config) error + SetMonitor(eventstream.LostEventCounter) + Start(*sync.WaitGroup) error + Pause() error + Resume() error +} + +var ( + // defaultEventTypes event types used whatever the event handlers or the rules + defaultEventTypes = []eval.EventType{ + model.ForkEventType.String(), + model.ExecEventType.String(), + model.ExitEventType.String(), + } +) + +// EBPFProbe defines a platform probe +type EBPFProbe struct { + Resolvers *resolvers.EBPFResolvers + + // Constants and configuration + opts Opts + config *config.Config + statsdClient statsd.ClientInterface + + probe *Probe + Manager *manager.Manager + managerOptions manager.Options + kernelVersion *kernel.Version + + // internals + monitors *EBPFMonitors + profileManagers *SecurityProfileManagers + fieldHandlers *EBPFFieldHandlers + + ctx context.Context + cancelFnc context.CancelFunc + wg sync.WaitGroup + + // Ring + eventStream EventStream + + // ActivityDumps section + activityDumpHandler dump.ActivityDumpHandler + + // Approvers / discarders section + Erpc *erpc.ERPC + erpcRequest *erpc.Request + inodeDiscarders *inodeDiscarders + discarderPushedCallbacks []DiscarderPushedCallback + approvers map[eval.EventType]kfilters.ActiveApprovers + + // Approvers / discarders section + discarderPushedCallbacksLock sync.RWMutex + discarderRateLimiter *rate.Limiter + + killListMap *lib.Map + supportsBPFSendSignal bool + + isRuntimeDiscarded bool + constantOffsets map[string]uint64 + runtimeCompiled bool + useFentry bool +} + +func (p *EBPFProbe) detectKernelVersion() error { + kernelVersion, err := kernel.NewKernelVersion() + if err != nil { + return fmt.Errorf("unable to detect the kernel version: %w", err) + } + p.kernelVersion = kernelVersion + return nil +} + +// GetKernelVersion computes and returns the running kernel version +func (p *EBPFProbe) GetKernelVersion() *kernel.Version { + return p.kernelVersion +} + +// UseRingBuffers returns true if eBPF ring buffers are supported and used +func (p *EBPFProbe) UseRingBuffers() bool { + return p.config.Probe.EventStreamUseRingBuffer && p.kernelVersion.HaveRingBuffers() +} + +func (p *EBPFProbe) selectFentryMode() { + if !p.config.Probe.EventStreamUseFentry { + p.useFentry = false + return + } + + supported := p.kernelVersion.HaveFentrySupport() + if !supported { + seclog.Errorf("fentry enabled but not supported, falling back to kprobe mode") + } + p.useFentry = supported +} + +func (p *EBPFProbe) sanityChecks() error { + // make sure debugfs is mounted + if _, err := tracefs.Root(); err != nil { + return err + } + + if utilkernel.GetLockdownMode() == utilkernel.Confidentiality { + return errors.New("eBPF not supported in lockdown `confidentiality` mode") + } + + if p.config.Probe.NetworkEnabled && p.kernelVersion.IsRH7Kernel() { + seclog.Warnf("The network feature of CWS isn't supported on Centos7, setting runtime_security_config.network.enabled to false") + p.config.Probe.NetworkEnabled = false + } + + return nil +} + +// NewModel returns a new Model +func (p *EBPFProbe) NewModel() *model.Model { + return NewEBPFModel(p) +} + +// VerifyOSVersion returns an error if the current kernel version is not supported +func (p *EBPFProbe) VerifyOSVersion() error { + if !p.kernelVersion.IsRH7Kernel() && !p.kernelVersion.IsRH8Kernel() && p.kernelVersion.Code < kernel.Kernel4_15 { + return fmt.Errorf("the following kernel is not supported: %s", p.kernelVersion) + } + return nil +} + +// VerifyEnvironment returns an error if the current environment seems to be misconfigured +func (p *EBPFProbe) VerifyEnvironment() *multierror.Error { + var err *multierror.Error + if aconfig.IsContainerized() { + if mounted, _ := mountinfo.Mounted("/etc/passwd"); !mounted { + err = multierror.Append(err, errors.New("/etc/passwd doesn't seem to be a mountpoint")) + } + + if mounted, _ := mountinfo.Mounted("/etc/group"); !mounted { + err = multierror.Append(err, errors.New("/etc/group doesn't seem to be a mountpoint")) + } + + if mounted, _ := mountinfo.Mounted(utilkernel.ProcFSRoot()); !mounted { + err = multierror.Append(err, errors.New("/etc/group doesn't seem to be a mountpoint")) + } + + if mounted, _ := mountinfo.Mounted(p.kernelVersion.OsReleasePath); !mounted { + err = multierror.Append(err, fmt.Errorf("%s doesn't seem to be a mountpoint", p.kernelVersion.OsReleasePath)) + } + + securityFSPath := filepath.Join(utilkernel.SysFSRoot(), "kernel/security") + if mounted, _ := mountinfo.Mounted(securityFSPath); !mounted { + err = multierror.Append(err, fmt.Errorf("%s doesn't seem to be a mountpoint", securityFSPath)) + } + + capsEffective, _, capErr := utils.CapEffCapEprm(utils.Getpid()) + if capErr != nil { + err = multierror.Append(capErr, errors.New("failed to get process capabilities")) + } else { + requiredCaps := []string{ + "CAP_SYS_ADMIN", + "CAP_SYS_RESOURCE", + "CAP_SYS_PTRACE", + "CAP_NET_ADMIN", + "CAP_NET_BROADCAST", + "CAP_NET_RAW", + "CAP_IPC_LOCK", + "CAP_CHOWN", + } + + for _, requiredCap := range requiredCaps { + capConst := model.KernelCapabilityConstants[requiredCap] + if capsEffective&capConst == 0 { + err = multierror.Append(err, fmt.Errorf("%s capability is missing", requiredCap)) + } + } + } + } + + return err +} + +// Init initializes the probe +func (p *EBPFProbe) Init() error { + useSyscallWrapper, err := ebpf.IsSyscallWrapperRequired() + if err != nil { + return err + } + + loader := ebpf.NewProbeLoader(p.config.Probe, useSyscallWrapper, p.UseRingBuffers(), p.useFentry, p.statsdClient) + defer loader.Close() + + bytecodeReader, runtimeCompiled, err := loader.Load() + if err != nil { + return err + } + defer bytecodeReader.Close() + + p.runtimeCompiled = runtimeCompiled + + if err := p.eventStream.Init(p.Manager, p.config.Probe); err != nil { + return err + } + + if p.isRuntimeDiscarded { + p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, manager.ConstantEditor{ + Name: "runtime_discarded", + Value: uint64(1), + }) + } + + p.managerOptions.ActivatedProbes = append(p.managerOptions.ActivatedProbes, probes.SnapshotSelectors(p.useFentry)...) + + if err := p.Manager.InitWithOptions(bytecodeReader, p.managerOptions); err != nil { + return fmt.Errorf("failed to init manager: %w", err) + } + + p.inodeDiscarders = newInodeDiscarders(p.Erpc, p.Resolvers.DentryResolver) + + if err := p.Resolvers.Start(p.ctx); err != nil { + return err + } + + err = p.monitors.Init() + if err != nil { + return err + } + + p.profileManagers, err = NewSecurityProfileManagers(p) + if err != nil { + return err + } + p.profileManagers.AddActivityDumpHandler(p.activityDumpHandler) + + p.eventStream.SetMonitor(p.monitors.eventStreamMonitor) + + p.killListMap, err = managerhelper.Map(p.Manager, "kill_list") + if err != nil { + return err + } + + return nil +} + +// IsRuntimeCompiled returns true if the eBPF programs where successfully runtime compiled +func (p *EBPFProbe) IsRuntimeCompiled() bool { + return p.runtimeCompiled +} + +// Setup the probe +func (p *EBPFProbe) Setup() error { + if err := p.Manager.Start(); err != nil { + return err + } + ebpfcheck.AddNameMappings(p.Manager, "cws") + + p.applyDefaultFilterPolicies() + + needRawSyscalls := p.isNeededForActivityDump(model.SyscallsEventType.String()) + + if err := p.updateProbes(defaultEventTypes, needRawSyscalls); err != nil { + return err + } + + p.profileManagers.Start(p.ctx, &p.wg) + + return nil +} + +// Start the probe +func (p *EBPFProbe) Start() error { + // Apply rules to the snapshotted data before starting the event stream to avoid concurrency issues + p.playSnapshot() + return p.eventStream.Start(&p.wg) +} + +// playSnapshot plays a snapshot +func (p *EBPFProbe) playSnapshot() { + // Get the snapshotted data + var events []*model.Event + + entryToEvent := func(entry *model.ProcessCacheEntry) { + if entry.Source != model.ProcessCacheEntryFromSnapshot { + return + } + entry.Retain() + event := NewEBPFEvent(p.fieldHandlers) + event.Type = uint32(model.ExecEventType) + event.TimestampRaw = uint64(time.Now().UnixNano()) + event.ProcessCacheEntry = entry + event.ProcessContext = &entry.ProcessContext + event.Exec.Process = &entry.Process + event.ProcessContext.Process.ContainerID = entry.ContainerID + + if _, err := entry.HasValidLineage(); err != nil { + event.Error = &model.ErrProcessBrokenLineage{Err: err} + } + + events = append(events, event) + } + p.Resolvers.ProcessResolver.Walk(entryToEvent) + for _, event := range events { + p.DispatchEvent(event) + event.ProcessCacheEntry.Release() + } +} + +func (p *EBPFProbe) sendAnomalyDetection(event *model.Event) { + tags := p.probe.GetEventTags(event.ContainerContext.ID) + if service := p.probe.GetService(event); service != "" { + tags = append(tags, "service:"+service) + } + + p.probe.DispatchCustomEvent( + events.NewCustomRule(events.AnomalyDetectionRuleID, events.AnomalyDetectionRuleDesc), + events.NewCustomEventLazy(event.GetEventType(), p.EventMarshallerCtor(event), tags...), + ) +} + +// AddActivityDumpHandler set the probe activity dump handler +func (p *EBPFProbe) AddActivityDumpHandler(handler dump.ActivityDumpHandler) { + p.activityDumpHandler = handler +} + +// DispatchEvent sends an event to the probe event handler +func (p *EBPFProbe) DispatchEvent(event *model.Event) { + traceEvent("Dispatching event %s", func() ([]byte, model.EventType, error) { + eventJSON, err := serializers.MarshalEvent(event) + return eventJSON, event.GetEventType(), err + }) + + // filter out event if already present on a profile + if p.config.RuntimeSecurity.SecurityProfileEnabled { + p.profileManagers.securityProfileManager.LookupEventInProfiles(event) + } + + // send event to wildcard handlers, like the CWS rule engine, first + p.probe.sendEventToWildcardHandlers(event) + + // send event to specific event handlers, like the event monitor consumers, subsequently + p.probe.sendEventToSpecificEventTypeHandlers(event) + + // handle anomaly detections + if event.IsAnomalyDetectionEvent() { + if event.IsKernelSpaceAnomalyDetectionEvent() { + p.profileManagers.securityProfileManager.FillProfileContextFromContainerID(event.FieldHandlers.ResolveContainerID(event, event.ContainerContext), &event.SecurityProfileContext) + } + if p.config.RuntimeSecurity.AnomalyDetectionEnabled { + p.sendAnomalyDetection(event) + } + } else if event.Error == nil { + // Process event after evaluation because some monitors need the DentryResolver to have been called first. + if p.profileManagers.activityDumpManager != nil { + p.profileManagers.activityDumpManager.ProcessEvent(event) + } + } + p.monitors.ProcessEvent(event) +} + +// SendStats sends statistics about the probe to Datadog +func (p *EBPFProbe) SendStats() error { + p.Resolvers.TCResolver.SendTCProgramsStats(p.statsdClient) + + if err := p.profileManagers.SendStats(); err != nil { + return err + } + + return p.monitors.SendStats() +} + +// GetMonitors returns the monitor of the probe +func (p *EBPFProbe) GetMonitors() *EBPFMonitors { + return p.monitors +} + +// EventMarshallerCtor returns the event marshaller ctor +func (p *EBPFProbe) EventMarshallerCtor(event *model.Event) func() events.EventMarshaler { + return func() events.EventMarshaler { + return serializers.NewEventSerializer(event) + } +} + +func (p *EBPFProbe) unmarshalContexts(data []byte, event *model.Event) (int, error) { + read, err := model.UnmarshalBinary(data, &event.PIDContext, &event.SpanContext, event.ContainerContext) + if err != nil { + return 0, err + } + + return read, nil +} + +func eventWithNoProcessContext(eventType model.EventType) bool { + return eventType == model.DNSEventType || eventType == model.LoadModuleEventType || eventType == model.UnloadModuleEventType +} + +func (p *EBPFProbe) unmarshalProcessCacheEntry(ev *model.Event, data []byte) (int, error) { + entry := p.Resolvers.ProcessResolver.NewProcessCacheEntry(ev.PIDContext) + ev.ProcessCacheEntry = entry + + n, err := entry.Process.UnmarshalBinary(data) + if err != nil { + return n, err + } + entry.Process.ContainerID = ev.ContainerContext.ID + entry.Source = model.ProcessCacheEntryFromEvent + + return n, nil +} + +func (p *EBPFProbe) onEventLost(perfMapName string, perEvent map[string]uint64) { + if p.config.RuntimeSecurity.InternalMonitoringEnabled { + p.probe.DispatchCustomEvent( + NewEventLostWriteEvent(perfMapName, perEvent), + ) + } + + // snapshot traced cgroups if a CgroupTracing event was lost + if p.probe.IsActivityDumpEnabled() && perEvent[model.CgroupTracingEventType.String()] > 0 { + p.profileManagers.SnapshotTracedCgroups() + } +} + +// setProcessContext set the process context, should return false if the event shouldn't be dispatched +func (p *EBPFProbe) setProcessContext(eventType model.EventType, event *model.Event) bool { + entry, isResolved := p.fieldHandlers.ResolveProcessCacheEntry(event) + event.ProcessCacheEntry = entry + if event.ProcessCacheEntry == nil { + panic("should always return a process cache entry") + } + + // use ProcessCacheEntry process context as process context + event.ProcessContext = &event.ProcessCacheEntry.ProcessContext + if event.ProcessContext == nil { + panic("should always return a process context") + } + + if process.IsKThread(event.ProcessContext.PPid, event.ProcessContext.Pid) { + return false + } + + if !eventWithNoProcessContext(eventType) { + if !isResolved { + event.Error = &model.ErrNoProcessContext{Err: errors.New("process context not resolved")} + } else if _, err := entry.HasValidLineage(); err != nil { + event.Error = &model.ErrProcessBrokenLineage{Err: err} + p.Resolvers.ProcessResolver.CountBrokenLineage() + } + } + + // flush exited process + p.Resolvers.ProcessResolver.DequeueExited() + + return true +} + +func (p *EBPFProbe) handleEvent(CPU int, data []byte) { + offset := 0 + event := p.probe.zeroEvent() + + dataLen := uint64(len(data)) + + read, err := event.UnmarshalBinary(data) + if err != nil { + seclog.Errorf("failed to decode event: %s", err) + return + } + offset += read + + eventType := event.GetEventType() + if eventType > model.MaxKernelEventType { + seclog.Errorf("unsupported event type %d", eventType) + return + } + + p.monitors.eventStreamMonitor.CountEvent(eventType, event.TimestampRaw, 1, dataLen, eventstream.EventStreamMap, CPU) + + // no need to dispatch events + switch eventType { + case model.MountReleasedEventType: + if _, err = event.MountReleased.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode mount released event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + + // Remove all dentry entries belonging to the mountID + p.Resolvers.DentryResolver.DelCacheEntries(event.MountReleased.MountID) + + // Delete new mount point from cache + if err = p.Resolvers.MountResolver.Delete(event.MountReleased.MountID); err != nil { + seclog.Tracef("failed to delete mount point %d from cache: %s", event.MountReleased.MountID, err) + } + return + case model.ArgsEnvsEventType: + if _, err = event.ArgsEnvs.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode args envs event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + + p.Resolvers.ProcessResolver.UpdateArgsEnvs(&event.ArgsEnvs) + + return + case model.CgroupTracingEventType: + if !p.config.RuntimeSecurity.ActivityDumpEnabled { + seclog.Errorf("shouldn't receive Cgroup event if activity dumps are disabled") + return + } + + if _, err = event.CgroupTracing.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode cgroup tracing event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + + p.profileManagers.activityDumpManager.HandleCGroupTracingEvent(&event.CgroupTracing) + return + case model.UnshareMountNsEventType: + if _, err = event.UnshareMountNS.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode unshare mnt ns event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + if err := p.handleNewMount(event, &event.UnshareMountNS.Mount); err != nil { + seclog.Debugf("failed to handle new mount from unshare mnt ns event: %s", err) + } + return + } + + read, err = p.unmarshalContexts(data[offset:], event) + if err != nil { + seclog.Errorf("failed to decode event `%s`: %s", eventType, err) + return + } + offset += read + + // save netns handle if applicable + nsPath := utils.NetNSPathFromPid(event.PIDContext.Pid) + _, _ = p.Resolvers.NamespaceResolver.SaveNetworkNamespaceHandle(event.PIDContext.NetNS, nsPath) + + if model.GetEventTypeCategory(eventType.String()) == model.NetworkCategory { + if read, err = event.NetworkContext.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode Network Context") + } + offset += read + } + + // handle exec and fork before process context resolution as they modify the process context resolution + switch eventType { + case model.ForkEventType: + if _, err = p.unmarshalProcessCacheEntry(event, data[offset:]); err != nil { + seclog.Errorf("failed to decode fork event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + + if process.IsKThread(event.ProcessCacheEntry.PPid, event.ProcessCacheEntry.Pid) { + return + } + + p.Resolvers.ProcessResolver.ApplyBootTime(event.ProcessCacheEntry) + event.ProcessCacheEntry.SetSpan(event.SpanContext.SpanID, event.SpanContext.TraceID) + + p.Resolvers.ProcessResolver.AddForkEntry(event.ProcessCacheEntry, event.PIDContext.ExecInode) + case model.ExecEventType: + // unmarshal and fill event.processCacheEntry + if _, err = p.unmarshalProcessCacheEntry(event, data[offset:]); err != nil { + seclog.Errorf("failed to decode exec event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + + if err = p.Resolvers.ProcessResolver.ResolveNewProcessCacheEntry(event.ProcessCacheEntry, event.ContainerContext); err != nil { + seclog.Debugf("failed to resolve new process cache entry context for pid %d: %s", event.PIDContext.Pid, err) + + var errResolution *path.ErrPathResolution + if errors.As(err, &errResolution) { + event.SetPathResolutionError(&event.ProcessCacheEntry.FileEvent, err) + } + } else { + p.Resolvers.ProcessResolver.AddExecEntry(event.ProcessCacheEntry, event.PIDContext.ExecInode) + } + + event.Exec.Process = &event.ProcessCacheEntry.Process + } + + if !p.setProcessContext(eventType, event) { + return + } + + switch eventType { + case model.FileMountEventType: + if _, err = event.Mount.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode mount event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + if err := p.handleNewMount(event, &event.Mount.Mount); err != nil { + seclog.Debugf("failed to handle new mount from mount event: %s\n", err) + return + } + + // TODO: this should be moved in the resolver itself in order to handle the fallbacks + if event.Mount.GetFSType() == "nsfs" { + nsid := uint32(event.Mount.RootPathKey.Inode) + mountPath, err := p.Resolvers.MountResolver.ResolveMountPath(event.Mount.MountID, event.Mount.Device, event.PIDContext.Pid, event.ContainerContext.ID) + if err != nil { + seclog.Debugf("failed to get mount path: %v", err) + } else { + mountNetNSPath := utils.NetNSPathFromPath(mountPath) + _, _ = p.Resolvers.NamespaceResolver.SaveNetworkNamespaceHandle(nsid, mountNetNSPath) + } + } + + case model.FileUmountEventType: + if _, err = event.Umount.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode umount event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + + // we can skip this error as this is for the umount only and there is no impact on the filepath resolution + mount, _ := p.Resolvers.MountResolver.ResolveMount(event.Umount.MountID, 0, event.PIDContext.Pid, event.ContainerContext.ID) + if mount != nil && mount.GetFSType() == "nsfs" { + nsid := uint32(mount.RootPathKey.Inode) + if namespace := p.Resolvers.NamespaceResolver.ResolveNetworkNamespace(nsid); namespace != nil { + p.FlushNetworkNamespace(namespace) + } + } + + case model.FileOpenEventType: + if _, err = event.Open.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode open event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + case model.FileMkdirEventType: + if _, err = event.Mkdir.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode mkdir event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + case model.FileRmdirEventType: + if _, err = event.Rmdir.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode rmdir event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + case model.FileUnlinkEventType: + if _, err = event.Unlink.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode unlink event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + case model.FileRenameEventType: + if _, err = event.Rename.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode rename event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + case model.FileChmodEventType: + if _, err = event.Chmod.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode chmod event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + case model.FileChownEventType: + if _, err = event.Chown.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode chown event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + case model.FileUtimesEventType: + if _, err = event.Utimes.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode utime event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + case model.FileLinkEventType: + if _, err = event.Link.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode link event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + case model.FileSetXAttrEventType: + if _, err = event.SetXAttr.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode setxattr event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + case model.FileRemoveXAttrEventType: + if _, err = event.RemoveXAttr.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode removexattr event: %s (offset %d, len %d)", err, offset, dataLen) + return + } + case model.ExitEventType: + if _, err = event.Exit.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode exit event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + + var exists bool + event.ProcessCacheEntry, exists = p.fieldHandlers.GetProcessCacheEntry(event) + if !exists { + // no need to dispatch an exit event that don't have the corresponding cache entry + return + } + + // Use the event timestamp as exit time + // The local process cache hasn't been updated yet with the exit time when the exit event is first seen + // The pid_cache kernel map has the exit_time but it's only accessed if there's a local miss + event.ProcessCacheEntry.Process.ExitTime = p.fieldHandlers.ResolveEventTime(event, &event.BaseEvent) + event.Exit.Process = &event.ProcessCacheEntry.Process + + // update mount pid mapping + p.Resolvers.MountResolver.DelPid(event.Exit.Pid) + case model.SetuidEventType: + // the process context may be incorrect, do not modify it + if event.Error != nil { + break + } + + if _, err = event.SetUID.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode setuid event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + defer p.Resolvers.ProcessResolver.UpdateUID(event.PIDContext.Pid, event) + case model.SetgidEventType: + // the process context may be incorrect, do not modify it + if event.Error != nil { + break + } + + if _, err = event.SetGID.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode setgid event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + defer p.Resolvers.ProcessResolver.UpdateGID(event.PIDContext.Pid, event) + case model.CapsetEventType: + // the process context may be incorrect, do not modify it + if event.Error != nil { + break + } + + if _, err = event.Capset.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode capset event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + defer p.Resolvers.ProcessResolver.UpdateCapset(event.PIDContext.Pid, event) + case model.SELinuxEventType: + if _, err = event.SELinux.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode selinux event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + case model.BPFEventType: + if _, err = event.BPF.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode bpf event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + case model.PTraceEventType: + if _, err = event.PTrace.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode ptrace event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + // resolve tracee process context + var pce *model.ProcessCacheEntry + if event.PTrace.PID > 0 { // pid can be 0 for a PTRACE_TRACEME request + pce = p.Resolvers.ProcessResolver.Resolve(event.PTrace.PID, event.PTrace.PID, 0, false) + } + if pce == nil { + pce = model.NewPlaceholderProcessCacheEntry(event.PTrace.PID, event.PTrace.PID, false) + } + event.PTrace.Tracee = &pce.ProcessContext + case model.MMapEventType: + if _, err = event.MMap.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode mmap event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + + if event.MMap.Flags&unix.MAP_ANONYMOUS != 0 { + // no need to trigger a dentry resolver, not backed by any file + event.MMap.File.SetPathnameStr("") + event.MMap.File.SetBasenameStr("") + } + case model.MProtectEventType: + if _, err = event.MProtect.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode mprotect event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + case model.LoadModuleEventType: + if _, err = event.LoadModule.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode load_module event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + + if event.LoadModule.LoadedFromMemory { + // no need to trigger a dentry resolver, not backed by any file + event.LoadModule.File.SetPathnameStr("") + event.LoadModule.File.SetBasenameStr("") + } + case model.UnloadModuleEventType: + if _, err = event.UnloadModule.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode unload_module event: %s (offset %d, len %d)", err, offset, len(data)) + } + case model.SignalEventType: + if _, err = event.Signal.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode signal event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + // resolve target process context + var pce *model.ProcessCacheEntry + if event.Signal.PID > 0 { // Linux accepts a kill syscall with both negative and zero pid + pce = p.Resolvers.ProcessResolver.Resolve(event.Signal.PID, event.Signal.PID, 0, false) + } + if pce == nil { + pce = model.NewPlaceholderProcessCacheEntry(event.Signal.PID, event.Signal.PID, false) + } + event.Signal.Target = &pce.ProcessContext + case model.SpliceEventType: + if _, err = event.Splice.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode splice event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + case model.NetDeviceEventType: + if _, err = event.NetDevice.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode net_device event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + _ = p.setupNewTCClassifier(event.NetDevice.Device) + case model.VethPairEventType: + if _, err = event.VethPair.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode veth_pair event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + _ = p.setupNewTCClassifier(event.VethPair.PeerDevice) + case model.DNSEventType: + if _, err = event.DNS.UnmarshalBinary(data[offset:]); err != nil { + if errors.Is(err, model.ErrDNSNameMalformatted) { + seclog.Debugf("failed to validate DNS event: %s", event.DNS.Name) + } else if errors.Is(err, model.ErrDNSNamePointerNotSupported) { + seclog.Tracef("failed to decode DNS event: %s (offset %d, len %d)", err, offset, len(data)) + } else { + seclog.Errorf("failed to decode DNS event: %s (offset %d, len %d)", err, offset, len(data)) + } + + return + } + case model.BindEventType: + if _, err = event.Bind.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode bind event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + case model.SyscallsEventType: + if _, err = event.Syscalls.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode syscalls event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + case model.AnomalyDetectionSyscallEventType: + if _, err = event.AnomalyDetectionSyscallEvent.UnmarshalBinary(data[offset:]); err != nil { + seclog.Errorf("failed to decode anomaly detection for syscall event: %s (offset %d, len %d)", err, offset, len(data)) + return + } + } + + // resolve the container context + event.ContainerContext, _ = p.fieldHandlers.ResolveContainerContext(event) + + p.DispatchEvent(event) + + if eventType == model.ExitEventType { + p.Resolvers.ProcessResolver.DeleteEntry(event.ProcessContext.Pid, event.ResolveEventTime()) + } +} + +// AddDiscarderPushedCallback add a callback to the list of func that have to be called when a discarder is pushed to kernel +func (p *EBPFProbe) AddDiscarderPushedCallback(cb DiscarderPushedCallback) { + p.discarderPushedCallbacksLock.Lock() + defer p.discarderPushedCallbacksLock.Unlock() + + p.discarderPushedCallbacks = append(p.discarderPushedCallbacks, cb) +} + +// GetEventTags returns the event tags +func (p *EBPFProbe) GetEventTags(containerID string) []string { + return p.Resolvers.TagsResolver.Resolve(containerID) +} + +// OnNewDiscarder handles new discarders +func (p *EBPFProbe) OnNewDiscarder(rs *rules.RuleSet, ev *model.Event, field eval.Field, eventType eval.EventType) { + // discarders disabled + if !p.config.Probe.EnableDiscarders { + return + } + + if p.isRuntimeDiscarded { + fakeTime := time.Unix(0, int64(ev.TimestampRaw)) + if !p.discarderRateLimiter.AllowN(fakeTime, 1) { + return + } + } + + seclog.Tracef("New discarder of type %s for field %s", eventType, field) + + if handlers, ok := allDiscarderHandlers[eventType]; ok { + for _, handler := range handlers { + discarderPushed, _ := handler(rs, ev, p, Discarder{Field: field}) + + if discarderPushed { + p.discarderPushedCallbacksLock.RLock() + defer p.discarderPushedCallbacksLock.RUnlock() + for _, cb := range p.discarderPushedCallbacks { + cb(eventType, ev, field) + } + } + } + } +} + +// ApplyFilterPolicy is called when a passing policy for an event type is applied +func (p *EBPFProbe) ApplyFilterPolicy(eventType eval.EventType, mode kfilters.PolicyMode, flags kfilters.PolicyFlag) error { + seclog.Infof("Setting in-kernel filter policy to `%s` for `%s`", mode, eventType) + table, err := managerhelper.Map(p.Manager, "filter_policy") + if err != nil { + return fmt.Errorf("unable to find policy table: %w", err) + } + + et := config.ParseEvalEventType(eventType) + if et == model.UnknownEventType { + return errors.New("unable to parse the eval event type") + } + + policy := &kfilters.FilterPolicy{ + Mode: mode, + Flags: flags, + } + + return table.Put(ebpf.Uint32MapItem(et), policy) +} + +// SetApprovers applies approvers and removes the unused ones +func (p *EBPFProbe) SetApprovers(eventType eval.EventType, approvers rules.Approvers) error { + handler, exists := kfilters.AllApproversHandlers[eventType] + if !exists { + return nil + } + + newApprovers, err := handler(approvers) + if err != nil { + seclog.Errorf("Error while adding approvers fallback in-kernel policy to `%s` for `%s`: %s", kfilters.PolicyModeAccept, eventType, err) + } + + type tag struct { + eventType eval.EventType + approverType string + } + approverAddedMetricCounter := make(map[tag]float64) + + for _, newApprover := range newApprovers { + seclog.Tracef("Applying approver %+v for event type %s", newApprover, eventType) + if err := newApprover.Apply(p.Manager); err != nil { + return err + } + + approverType := getApproverType(newApprover.GetTableName()) + approverAddedMetricCounter[tag{eventType, approverType}]++ + } + + if previousApprovers, exist := p.approvers[eventType]; exist { + previousApprovers.Sub(newApprovers) + for _, previousApprover := range previousApprovers { + seclog.Tracef("Removing previous approver %+v for event type %s", previousApprover, eventType) + if err := previousApprover.Remove(p.Manager); err != nil { + return err + } + + approverType := getApproverType(previousApprover.GetTableName()) + approverAddedMetricCounter[tag{eventType, approverType}]-- + if approverAddedMetricCounter[tag{eventType, approverType}] <= 0 { + delete(approverAddedMetricCounter, tag{eventType, approverType}) + } + } + } + + for tags, count := range approverAddedMetricCounter { + tags := []string{ + fmt.Sprintf("approver_type:%s", tags.approverType), + fmt.Sprintf("event_type:%s", tags.eventType), + } + + if err := p.statsdClient.Gauge(metrics.MetricApproverAdded, count, tags, 1.0); err != nil { + seclog.Tracef("couldn't set MetricApproverAdded metric: %s", err) + } + } + + p.approvers[eventType] = newApprovers + return nil +} + +func getApproverType(approverTableName string) string { + approverType := "flag" + + if approverTableName == kfilters.BasenameApproverKernelMapName { + approverType = "basename" + } + + return approverType +} + +func (p *EBPFProbe) isNeededForActivityDump(eventType eval.EventType) bool { + if p.config.RuntimeSecurity.ActivityDumpEnabled { + for _, e := range p.profileManagers.GetActivityDumpTracedEventTypes() { + if e.String() == eventType { + return true + } + } + } + return false +} + +func (p *EBPFProbe) isNeededForSecurityProfile(eventType eval.EventType) bool { + if p.config.RuntimeSecurity.SecurityProfileEnabled { + for _, e := range p.config.RuntimeSecurity.AnomalyDetectionEventTypes { + if e.String() == eventType { + return true + } + } + } + return false +} + +func (p *EBPFProbe) validEventTypeForConfig(eventType string) bool { + if eventType == "dns" && !p.config.Probe.NetworkEnabled { + return false + } + return true +} + +// updateProbes applies the loaded set of rules and returns a report +// of the applied approvers for it. +func (p *EBPFProbe) updateProbes(ruleEventTypes []eval.EventType, needRawSyscalls bool) error { + // event types enabled either by event handlers or by rules + eventTypes := append([]eval.EventType{}, defaultEventTypes...) + eventTypes = append(eventTypes, ruleEventTypes...) + for eventType, handlers := range p.probe.eventHandlers { + if len(handlers) == 0 { + continue + } + if slices.Contains(eventTypes, model.EventType(eventType).String()) { + continue + } + if eventType != int(model.UnknownEventType) && eventType != int(model.MaxAllEventType) { + eventTypes = append(eventTypes, model.EventType(eventType).String()) + } + } + + activatedProbes := probes.SnapshotSelectors(p.useFentry) + + // extract probe to activate per the event types + for eventType, selectors := range probes.GetSelectorsPerEventType(p.useFentry) { + if (eventType == "*" || slices.Contains(eventTypes, eventType) || p.isNeededForActivityDump(eventType) || p.isNeededForSecurityProfile(eventType)) && p.validEventTypeForConfig(eventType) { + activatedProbes = append(activatedProbes, selectors...) + } + } + + activatedProbes = append(activatedProbes, p.Resolvers.TCResolver.SelectTCProbes()) + + if needRawSyscalls { + activatedProbes = append(activatedProbes, probes.SyscallMonitorSelectors...) + } else { + // ActivityDumps + if p.config.RuntimeSecurity.ActivityDumpEnabled { + for _, e := range p.profileManagers.GetActivityDumpTracedEventTypes() { + if e == model.SyscallsEventType { + activatedProbes = append(activatedProbes, probes.SyscallMonitorSelectors...) + break + } + } + } + } + + // Print the list of unique probe identification IDs that are registered + var selectedIDs []manager.ProbeIdentificationPair + for _, selector := range activatedProbes { + for _, id := range selector.GetProbesIdentificationPairList() { + var exists bool + for _, selectedID := range selectedIDs { + if selectedID == id { + exists = true + } + } + if !exists { + selectedIDs = append(selectedIDs, id) + seclog.Tracef("probe %s selected", id) + } + } + } + + enabledEventsMap, err := managerhelper.Map(p.Manager, "enabled_events") + if err != nil { + return err + } + + enabledEvents := uint64(0) + for _, eventName := range eventTypes { + if eventName != "*" { + eventType := config.ParseEvalEventType(eventName) + if eventType == model.UnknownEventType { + return fmt.Errorf("unknown event type '%s'", eventName) + } + enabledEvents |= 1 << (eventType - 1) + } + } + + if err := enabledEventsMap.Put(ebpf.ZeroUint32MapItem, enabledEvents); err != nil { + return fmt.Errorf("failed to set enabled events: %w", err) + } + + return p.Manager.UpdateActivatedProbes(activatedProbes) +} + +// GetDiscarders retrieve the discarders +func (p *EBPFProbe) GetDiscarders() (*DiscardersDump, error) { + inodeMap, err := managerhelper.Map(p.Manager, "inode_discarders") + if err != nil { + return nil, err + } + + pidMap, err := managerhelper.Map(p.Manager, "pid_discarders") + if err != nil { + return nil, err + } + + statsFB, err := managerhelper.Map(p.Manager, "fb_discarder_stats") + if err != nil { + return nil, err + } + + statsBB, err := managerhelper.Map(p.Manager, "bb_discarder_stats") + if err != nil { + return nil, err + } + + dump, err := dumpDiscarders(p.Resolvers.DentryResolver, pidMap, inodeMap, statsFB, statsBB) + if err != nil { + return nil, err + } + return &dump, nil +} + +// DumpDiscarders dump the discarders +func (p *EBPFProbe) DumpDiscarders() (string, error) { + dump, err := p.GetDiscarders() + if err != nil { + return "", err + } + + fp, err := os.CreateTemp("/tmp", "discarder-dump-") + if err != nil { + return "", err + } + defer fp.Close() + + if err := os.Chmod(fp.Name(), 0400); err != nil { + return "", err + } + + encoder := yaml.NewEncoder(fp) + defer encoder.Close() + + if err := encoder.Encode(dump); err != nil { + return "", err + } + err = fp.Close() + if err != nil { + return "", fmt.Errorf("could not close file [%s]: %w", fp.Name(), err) + } + return fp.Name(), err +} + +// FlushDiscarders flush the discarders +func (p *EBPFProbe) FlushDiscarders() error { + return bumpDiscardersRevision(p.Erpc) +} + +// RefreshUserCache refreshes the user cache +func (p *EBPFProbe) RefreshUserCache(containerID string) error { + return p.Resolvers.UserGroupResolver.RefreshCache(containerID) +} + +// Snapshot runs the different snapshot functions of the resolvers that +// require to sync with the current state of the system +func (p *EBPFProbe) Snapshot() error { + // the snapshot for the read of a lot of file which can allocate a lot of memory. + defer runtime.GC() + return p.Resolvers.Snapshot() +} + +// Stop the probe +func (p *EBPFProbe) Stop() { + _ = p.Manager.StopReaders(manager.CleanAll) +} + +// Close the probe +func (p *EBPFProbe) Close() error { + // Cancelling the context will stop the reorderer = we won't dequeue events anymore and new events from the + // perf map reader are ignored + p.cancelFnc() + + // we wait until both the reorderer and the monitor are stopped + p.wg.Wait() + + ebpfcheck.RemoveNameMappings(p.Manager) + // Stopping the manager will stop the perf map reader and unload eBPF programs + if err := p.Manager.Stop(manager.CleanAll); err != nil { + return err + } + + // when we reach this point, we do not generate nor consume events anymore, we can close the resolvers + return p.Resolvers.Close() +} + +// QueuedNetworkDeviceError is used to indicate that the new network device was queued until its namespace handle is +// resolved. +type QueuedNetworkDeviceError struct { + msg string +} + +func (err QueuedNetworkDeviceError) Error() string { + return err.msg +} + +func (p *EBPFProbe) setupNewTCClassifier(device model.NetDevice) error { + // select netns handle + var handle *os.File + var err error + netns := p.Resolvers.NamespaceResolver.ResolveNetworkNamespace(device.NetNS) + if netns != nil { + handle, err = netns.GetNamespaceHandleDup() + } + if err != nil { + defer handle.Close() + } + if netns == nil || err != nil || handle == nil { + // queue network device so that a TC classifier can be added later + p.Resolvers.NamespaceResolver.QueueNetworkDevice(device) + return QueuedNetworkDeviceError{msg: fmt.Sprintf("device %s is queued until %d is resolved", device.Name, device.NetNS)} + } + err = p.Resolvers.TCResolver.SetupNewTCClassifierWithNetNSHandle(device, handle, p.Manager) + if err != nil { + return err + } + if handle != nil { + if err := handle.Close(); err != nil { + return fmt.Errorf("could not close file [%s]: %w", handle.Name(), err) + } + } + return err +} + +// FlushNetworkNamespace removes all references and stops all TC programs in the provided network namespace. This method +// flushes the network namespace in the network namespace resolver as well. +func (p *EBPFProbe) FlushNetworkNamespace(namespace *netns.NetworkNamespace) { + p.Resolvers.NamespaceResolver.FlushNetworkNamespace(namespace) + + // cleanup internal structures + p.Resolvers.TCResolver.FlushNetworkNamespaceID(namespace.ID(), p.Manager) +} + +func (p *EBPFProbe) handleNewMount(ev *model.Event, m *model.Mount) error { + // There could be entries of a previous mount_id in the cache for instance, + // runc does the following : it bind mounts itself (using /proc/exe/self), + // opens a file descriptor on the new file with O_CLOEXEC then umount the bind mount using + // MNT_DETACH. It then does an exec syscall, that will cause the fd to be closed. + // Our dentry resolution of the exec event causes the inode/mount_id to be put in cache, + // so we remove all dentry entries belonging to the mountID. + p.Resolvers.DentryResolver.DelCacheEntries(m.MountID) + + // Resolve mount point + if err := p.Resolvers.PathResolver.SetMountPoint(ev, m); err != nil { + seclog.Debugf("failed to set mount point: %v", err) + return err + } + // Resolve root + if err := p.Resolvers.PathResolver.SetMountRoot(ev, m); err != nil { + seclog.Debugf("failed to set mount root: %v", err) + return err + } + + // Insert new mount point in cache, passing it a copy of the mount that we got from the event + if err := p.Resolvers.MountResolver.Insert(*m, 0); err != nil { + seclog.Errorf("failed to insert mount event: %v", err) + return err + } + + return nil +} + +func (p *EBPFProbe) applyDefaultFilterPolicies() { + if !p.config.Probe.EnableKernelFilters { + seclog.Warnf("Forcing in-kernel filter policy to `pass`: filtering not enabled") + } + + for eventType := model.FirstEventType; eventType <= model.LastEventType; eventType++ { + var mode kfilters.PolicyMode + + if !p.config.Probe.EnableKernelFilters { + mode = kfilters.PolicyModeNoFilter + } else if len(p.probe.eventHandlers[eventType]) > 0 { + mode = kfilters.PolicyModeAccept + } else { + mode = kfilters.PolicyModeDeny + } + + if err := p.ApplyFilterPolicy(eventType.String(), mode, math.MaxUint8); err != nil { + seclog.Debugf("unable to apply to filter policy `%s` for `%s`", eventType, mode) + } + } +} + +// ApplyRuleSet apply the required update to handle the new ruleset +func (p *EBPFProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.ApplyRuleSetReport, error) { + if p.opts.SyscallsMonitorEnabled { + if err := p.monitors.syscallsMonitor.Disable(); err != nil { + return nil, err + } + } + + ars, err := kfilters.NewApplyRuleSetReport(p.config.Probe, rs) + if err != nil { + return nil, err + } + + for eventType, report := range ars.Policies { + if err := p.ApplyFilterPolicy(eventType, report.Mode, report.Flags); err != nil { + return nil, err + } + if err := p.SetApprovers(eventType, report.Approvers); err != nil { + return nil, err + } + } + + needRawSyscalls := p.isNeededForActivityDump(model.SyscallsEventType.String()) + if !needRawSyscalls { + // Add syscall monitor probes if it's either activated or + // there is an 'kill' action in the ruleset + for _, rule := range rs.GetRules() { + for _, action := range rule.Definition.Actions { + if action.Kill != nil { + needRawSyscalls = true + break + } + } + } + } + + if err := p.updateProbes(rs.GetEventTypes(), needRawSyscalls); err != nil { + return nil, fmt.Errorf("failed to select probes: %w", err) + } + + if p.opts.SyscallsMonitorEnabled { + if err := p.monitors.syscallsMonitor.Flush(); err != nil { + return nil, err + } + if err := p.monitors.syscallsMonitor.Enable(); err != nil { + return nil, err + } + } + + return ars, nil +} + +// NewEvent returns a new event +func (p *EBPFProbe) NewEvent() *model.Event { + return NewEBPFEvent(p.fieldHandlers) +} + +// GetFieldHandlers returns the field handlers +func (p *EBPFProbe) GetFieldHandlers() model.FieldHandlers { + return p.fieldHandlers +} + +// DumpProcessCache dumps the process cache +func (p *EBPFProbe) DumpProcessCache(withArgs bool) (string, error) { + return p.Resolvers.ProcessResolver.Dump(withArgs) +} + +// NewEBPFProbe instantiates a new runtime security agent probe +func NewEBPFProbe(probe *Probe, config *config.Config, opts Opts) (*EBPFProbe, error) { + nerpc, err := erpc.NewERPC() + if err != nil { + return nil, err + } + + ctx, cancelFnc := context.WithCancel(context.Background()) + + p := &EBPFProbe{ + probe: probe, + config: config, + opts: opts, + statsdClient: opts.StatsdClient, + discarderRateLimiter: rate.NewLimiter(rate.Every(time.Second/5), 100), + approvers: make(map[eval.EventType]kfilters.ActiveApprovers), + managerOptions: ebpf.NewDefaultOptions(), + Erpc: nerpc, + erpcRequest: erpc.NewERPCRequest(0), + isRuntimeDiscarded: !probe.Opts.DontDiscardRuntime, + ctx: ctx, + cancelFnc: cancelFnc, + } + + if err := p.detectKernelVersion(); err != nil { + // we need the kernel version to start, fail if we can't get it + return nil, err + } + + if err := p.sanityChecks(); err != nil { + return nil, err + } + + if err := p.VerifyOSVersion(); err != nil { + seclog.Warnf("the current kernel isn't officially supported, some features might not work properly: %v", err) + } + + if err := p.VerifyEnvironment(); err != nil { + seclog.Warnf("the current environment may be misconfigured: %v", err) + } + + p.selectFentryMode() + + useRingBuffers := p.UseRingBuffers() + useMmapableMaps := p.kernelVersion.HaveMmapableMaps() + + p.Manager = ebpf.NewRuntimeSecurityManager(useRingBuffers, p.useFentry) + + p.supportsBPFSendSignal = p.kernelVersion.SupportBPFSendSignal() + + p.ensureConfigDefaults() + + p.monitors = NewEBPFMonitors(p) + + numCPU, err := utils.NumCPU() + if err != nil { + return nil, fmt.Errorf("failed to parse CPU count: %w", err) + } + + p.managerOptions.MapSpecEditors = probes.AllMapSpecEditors(numCPU, probes.MapSpecEditorOpts{ + TracedCgroupSize: config.RuntimeSecurity.ActivityDumpTracedCgroupsCount, + UseRingBuffers: useRingBuffers, + UseMmapableMaps: useMmapableMaps, + RingBufferSize: uint32(config.Probe.EventStreamBufferSize), + PathResolutionEnabled: probe.Opts.PathResolutionEnabled, + SecurityProfileMaxCount: config.RuntimeSecurity.SecurityProfileMaxCount, + }) + + if config.RuntimeSecurity.ActivityDumpEnabled { + for _, e := range config.RuntimeSecurity.ActivityDumpTracedEventTypes { + if e == model.SyscallsEventType { + // Add syscall monitor probes + p.managerOptions.ActivatedProbes = append(p.managerOptions.ActivatedProbes, probes.SyscallMonitorSelectors...) + break + } + } + } + + p.constantOffsets, err = p.GetOffsetConstants() + if err != nil { + seclog.Warnf("constant fetcher failed: %v", err) + return nil, err + } + + p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, constantfetch.CreateConstantEditors(p.constantOffsets)...) + + areCGroupADsEnabled := config.RuntimeSecurity.ActivityDumpTracedCgroupsCount > 0 + + // Add global constant editors + p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, + manager.ConstantEditor{ + Name: "runtime_pid", + Value: uint64(utils.Getpid()), + }, + manager.ConstantEditor{ + Name: "do_fork_input", + Value: getDoForkInput(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "has_usernamespace_first_arg", + Value: getHasUsernamespaceFirstArg(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "ovl_path_in_ovl_inode", + Value: getOvlPathInOvlInode(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "mount_id_offset", + Value: mount.GetMountIDOffset(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "getattr2", + Value: getAttr2(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "vfs_unlink_dentry_position", + Value: mount.GetVFSLinkDentryPosition(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "vfs_mkdir_dentry_position", + Value: mount.GetVFSMKDirDentryPosition(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "vfs_link_target_dentry_position", + Value: mount.GetVFSLinkTargetDentryPosition(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "vfs_setxattr_dentry_position", + Value: mount.GetVFSSetxattrDentryPosition(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "vfs_removexattr_dentry_position", + Value: mount.GetVFSRemovexattrDentryPosition(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "vfs_rename_input_type", + Value: mount.GetVFSRenameInputType(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "check_helper_call_input", + Value: getCheckHelperCallInputType(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "cgroup_activity_dumps_enabled", + Value: utils.BoolTouint64(config.RuntimeSecurity.ActivityDumpEnabled && areCGroupADsEnabled), + }, + manager.ConstantEditor{ + Name: "net_struct_type", + Value: getNetStructType(p.kernelVersion), + }, + manager.ConstantEditor{ + Name: "syscall_monitor_event_period", + Value: uint64(config.RuntimeSecurity.ActivityDumpSyscallMonitorPeriod.Nanoseconds()), + }, + manager.ConstantEditor{ + Name: "send_signal", + Value: utils.BoolTouint64(p.kernelVersion.SupportBPFSendSignal()), + }, + manager.ConstantEditor{ + Name: "anomaly_syscalls", + Value: utils.BoolTouint64(slices.Contains(config.RuntimeSecurity.AnomalyDetectionEventTypes, model.SyscallsEventType)), + }, + manager.ConstantEditor{ + Name: "monitor_syscalls_map_enabled", + Value: utils.BoolTouint64(probe.Opts.SyscallsMonitorEnabled), + }, + ) + + p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, DiscarderConstants...) + p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, getCGroupWriteConstants()) + + // if we are using tracepoints to probe syscall exits, i.e. if we are using an old kernel version (< 4.12) + // we need to use raw_syscall tracepoints for exits, as syscall are not trace when running an ia32 userspace + // process + if probes.ShouldUseSyscallExitTracepoints() { + p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, + manager.ConstantEditor{ + Name: "tracepoint_raw_syscall_fallback", + Value: utils.BoolTouint64(true), + }, + ) + } + + if useRingBuffers { + p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, + manager.ConstantEditor{ + Name: "use_ring_buffer", + Value: utils.BoolTouint64(true), + }, + ) + } + + if p.kernelVersion.HavePIDLinkStruct() { + p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, + manager.ConstantEditor{ + Name: "kernel_has_pid_link_struct", + Value: utils.BoolTouint64(true), + }, + ) + } + + if p.kernelVersion.HaveLegacyPipeInodeInfoStruct() { + p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, + manager.ConstantEditor{ + Name: "kernel_has_legacy_pipe_inode_info", + Value: utils.BoolTouint64(true), + }, + ) + } + + // tail calls + p.managerOptions.TailCallRouter = probes.AllTailRoutes(config.Probe.ERPCDentryResolutionEnabled, config.Probe.NetworkEnabled, useMmapableMaps, p.useFentry) + if !config.Probe.ERPCDentryResolutionEnabled || useMmapableMaps { + // exclude the programs that use the bpf_probe_write_user helper + p.managerOptions.ExcludedFunctions = probes.AllBPFProbeWriteUserProgramFunctions() + } + + if !config.Probe.NetworkEnabled { + // prevent all TC classifiers from loading + p.managerOptions.ExcludedFunctions = append(p.managerOptions.ExcludedFunctions, probes.GetAllTCProgramFunctions()...) + } + + if p.useFentry { + afBasedExcluder, err := newAvailableFunctionsBasedExcluder() + if err != nil { + return nil, err + } + + p.managerOptions.AdditionalExcludedFunctionCollector = afBasedExcluder + } + + resolversOpts := resolvers.Opts{ + PathResolutionEnabled: probe.Opts.PathResolutionEnabled, + TagsResolver: probe.Opts.TagsResolver, + UseRingBuffer: useRingBuffers, + TTYFallbackEnabled: probe.Opts.TTYFallbackEnabled, + } + + p.Resolvers, err = resolvers.NewEBPFResolvers(config, p.Manager, probe.StatsdClient, probe.scrubber, p.Erpc, resolversOpts) + if err != nil { + return nil, err + } + + // TODO safchain change the fields handlers + p.fieldHandlers = &EBPFFieldHandlers{resolvers: p.Resolvers} + + if useRingBuffers { + p.eventStream = ringbuffer.New(p.handleEvent) + p.managerOptions.SkipRingbufferReaderStartup = map[string]bool{ + eventstream.EventStreamMap: true, + } + } else { + p.eventStream, err = reorderer.NewOrderedPerfMap(p.ctx, p.handleEvent, probe.StatsdClient) + if err != nil { + return nil, err + } + p.managerOptions.SkipPerfMapReaderStartup = map[string]bool{ + eventstream.EventStreamMap: true, + } + } + + return p, nil +} + +// GetProfileManagers returns the security profile managers +func (p *EBPFProbe) GetProfileManagers() *SecurityProfileManagers { + return p.profileManagers +} + +func (p *EBPFProbe) ensureConfigDefaults() { + // enable runtime compiled constants on COS by default + if !p.config.Probe.RuntimeCompiledConstantsIsSet && p.kernelVersion.IsCOSKernel() { + p.config.Probe.RuntimeCompiledConstantsEnabled = true + } +} + +const ( + netStructHasProcINum uint64 = 0 + netStructHasNS uint64 = 1 +) + +// getNetStructType returns whether the net structure has a namespace attribute +func getNetStructType(kv *kernel.Version) uint64 { + if kv.IsRH7Kernel() { + return netStructHasProcINum + } + return netStructHasNS +} + +const ( + doForkListInput uint64 = iota + doForkStructInput +) + +func getAttr2(kernelVersion *kernel.Version) uint64 { + if kernelVersion.IsRH7Kernel() { + return 1 + } + return 0 +} + +// getDoForkInput returns the expected input type of _do_fork, do_fork and kernel_clone +func getDoForkInput(kernelVersion *kernel.Version) uint64 { + if kernelVersion.Code != 0 && kernelVersion.Code >= kernel.Kernel5_3 { + return doForkStructInput + } + return doForkListInput +} + +func getHasUsernamespaceFirstArg(kernelVersion *kernel.Version) uint64 { + if kernelVersion.Code != 0 && kernelVersion.Code >= kernel.Kernel6_0 { + return 1 + } + return 0 +} + +func getOvlPathInOvlInode(kernelVersion *kernel.Version) uint64 { + // https://github.com/torvalds/linux/commit/0af950f57fefabab628f1963af881e6b9bfe7f38 + if kernelVersion.Code != 0 && kernelVersion.Code >= kernel.Kernel6_5 { + return 2 + } + + // https://github.com/torvalds/linux/commit/ffa5723c6d259b3191f851a50a98d0352b345b39 + // changes a bit how the lower dentry/inode is stored in `ovl_inode`. To check if we + // are in this configuration we first probe the kernel version, then we check for the + // presence of the function introduced in the same patch. + const patchSentinel = "ovl_i_path_real" + + if kernelVersion.Code != 0 && kernelVersion.Code >= kernel.Kernel5_19 { + return 1 + } + + check, err := commonebpf.VerifyKernelFuncs(patchSentinel) + if err != nil { + return 0 + } + + // VerifyKernelFuncs returns the missing functions + if _, ok := check[patchSentinel]; !ok { + return 1 + } + + return 0 +} + +// getCGroupWriteConstants returns the value of the constant used to determine how cgroups should be captured in kernel +// space +func getCGroupWriteConstants() manager.ConstantEditor { + cgroupWriteConst := uint64(1) + kv, err := kernel.NewKernelVersion() + if err == nil { + if kv.IsRH7Kernel() { + cgroupWriteConst = 2 + } + } + + return manager.ConstantEditor{ + Name: "cgroup_write_type", + Value: cgroupWriteConst, + } +} + +// GetOffsetConstants returns the offsets and struct sizes constants +func (p *EBPFProbe) GetOffsetConstants() (map[string]uint64, error) { + constantFetcher := constantfetch.ComposeConstantFetchers(constantfetch.GetAvailableConstantFetchers(p.config.Probe, p.kernelVersion, p.statsdClient)) + AppendProbeRequestsToFetcher(constantFetcher, p.kernelVersion) + return constantFetcher.FinishAndGetResults() +} + +// GetConstantFetcherStatus returns the status of the constant fetcher associated with this probe +func (p *EBPFProbe) GetConstantFetcherStatus() (*constantfetch.ConstantFetcherStatus, error) { + constantFetcher := constantfetch.ComposeConstantFetchers(constantfetch.GetAvailableConstantFetchers(p.config.Probe, p.kernelVersion, p.statsdClient)) + AppendProbeRequestsToFetcher(constantFetcher, p.kernelVersion) + return constantFetcher.FinishAndGetStatus() +} + +// AppendProbeRequestsToFetcher returns the offsets and struct sizes constants, from a constant fetcher +func AppendProbeRequestsToFetcher(constantFetcher constantfetch.ConstantFetcher, kv *kernel.Version) { + constantFetcher.AppendSizeofRequest(constantfetch.SizeOfInode, "struct inode", "linux/fs.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameSuperBlockStructSFlags, "struct super_block", "s_flags", "linux/fs.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameSuperBlockStructSMagic, "struct super_block", "s_magic", "linux/fs.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameDentryStructDSB, "struct dentry", "d_sb", "linux/dcache.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameSignalStructStructTTY, "struct signal_struct", "tty", "linux/sched/signal.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameTTYStructStructName, "struct tty_struct", "name", "linux/tty.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameCredStructUID, "struct cred", "uid", "linux/cred.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameLinuxBinprmP, "struct linux_binprm", "p", "linux/binfmts.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameLinuxBinprmArgc, "struct linux_binprm", "argc", "linux/binfmts.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameLinuxBinprmEnvc, "struct linux_binprm", "envc", "linux/binfmts.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameVMAreaStructFlags, "struct vm_area_struct", "vm_flags", "linux/mm_types.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameFileFinode, "struct file", "f_inode", "linux/fs.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameFileFpath, "struct file", "f_path", "linux/fs.h") + if kv.Code >= kernel.Kernel5_3 { + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameKernelCloneArgsExitSignal, "struct kernel_clone_args", "exit_signal", "linux/sched/task.h") + } + + // bpf offsets + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFMapStructID, "struct bpf_map", "id", "linux/bpf.h") + if kv.Code != 0 && (kv.Code >= kernel.Kernel4_15 || kv.IsRH7Kernel()) { + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFMapStructName, "struct bpf_map", "name", "linux/bpf.h") + } + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFMapStructMapType, "struct bpf_map", "map_type", "linux/bpf.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFProgAuxStructID, "struct bpf_prog_aux", "id", "linux/bpf.h") + if kv.Code != 0 && (kv.Code >= kernel.Kernel4_15 || kv.IsRH7Kernel()) { + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFProgAuxStructName, "struct bpf_prog_aux", "name", "linux/bpf.h") + } + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFProgStructTag, "struct bpf_prog", "tag", "linux/filter.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFProgStructAux, "struct bpf_prog", "aux", "linux/filter.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFProgStructType, "struct bpf_prog", "type", "linux/filter.h") + + if kv.Code != 0 && (kv.Code > kernel.Kernel4_16 || kv.IsSuse12Kernel() || kv.IsSuse15Kernel()) { + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFProgStructExpectedAttachType, "struct bpf_prog", "expected_attach_type", "linux/filter.h") + } + // namespace nr offsets + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePIDStructLevel, "struct pid", "level", "linux/pid.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePIDStructNumbers, "struct pid", "numbers", "linux/pid.h") + constantFetcher.AppendSizeofRequest(constantfetch.SizeOfUPID, "struct upid", "linux/pid.h") + if kv.HavePIDLinkStruct() { + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameTaskStructPIDLink, "struct task_struct", "pids", "linux/sched.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePIDLinkStructPID, "struct pid_link", "pid", "linux/pid.h") + } else { + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameTaskStructPID, "struct task_struct", "thread_pid", "linux/sched.h") + } + + // splice event + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePipeInodeInfoStructBufs, "struct pipe_inode_info", "bufs", "linux/pipe_fs_i.h") + if kv.HaveLegacyPipeInodeInfoStruct() { + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePipeInodeInfoStructNrbufs, "struct pipe_inode_info", "nrbufs", "linux/pipe_fs_i.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePipeInodeInfoStructCurbuf, "struct pipe_inode_info", "curbuf", "linux/pipe_fs_i.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePipeInodeInfoStructBuffers, "struct pipe_inode_info", "buffers", "linux/pipe_fs_i.h") + } else { + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePipeInodeInfoStructHead, "struct pipe_inode_info", "head", "linux/pipe_fs_i.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePipeInodeInfoStructRingsize, "struct pipe_inode_info", "ring_size", "linux/pipe_fs_i.h") + } + + // network related constants + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameNetDeviceStructIfIndex, "struct net_device", "ifindex", "linux/netdevice.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameSockCommonStructSKCNet, "struct sock_common", "skc_net", "net/sock.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameSockCommonStructSKCFamily, "struct sock_common", "skc_family", "net/sock.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameFlowI4StructSADDR, "struct flowi4", "saddr", "net/flow.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameFlowI4StructULI, "struct flowi4", "uli", "net/flow.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameFlowI6StructSADDR, "struct flowi6", "saddr", "net/flow.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameFlowI6StructULI, "struct flowi6", "uli", "net/flow.h") + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameSocketStructSK, "struct socket", "sk", "linux/net.h") + + // Interpreter constants + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameLinuxBinprmStructFile, "struct linux_binprm", "file", "linux/binfmts.h") + + if !kv.IsRH7Kernel() { + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameNFConnStructCTNet, "struct nf_conn", "ct_net", "net/netfilter/nf_conntrack.h") + } + + if getNetStructType(kv) == netStructHasProcINum { + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameNetStructProcInum, "struct net", "proc_inum", "net/net_namespace.h") + } else { + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameNetStructNS, "struct net", "ns", "net/net_namespace.h") + } + + // iouring + if kv.Code != 0 && (kv.Code >= kernel.Kernel5_1) { + constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameIoKiocbStructCtx, "struct io_kiocb", "ctx", "") + } +} + +// HandleActions handles the rule actions +func (p *EBPFProbe) HandleActions(rule *rules.Rule, event eval.Event) { + ev := event.(*model.Event) + for _, action := range rule.Definition.Actions { + switch { + case action.InternalCallbackDefinition != nil && rule.ID == events.RefreshUserCacheRuleID: + _ = p.RefreshUserCache(ev.ContainerContext.ID) + + case action.Kill != nil: + if pid := ev.ProcessContext.Pid; pid > 1 && pid != utils.Getpid() { + log.Debugf("Requesting signal %s to be sent to %d", action.Kill.Signal, pid) + sig := model.SignalConstants[action.Kill.Signal] + + var err error + if p.supportsBPFSendSignal { + err = p.killListMap.Put(uint32(pid), uint32(sig)) + } else { + err = syscall.Kill(int(pid), syscall.Signal(sig)) + } + if err != nil { + seclog.Warnf("failed to kill process %d: %s", pid, err) + } + } + } + } +} diff --git a/pkg/security/probe/probe_linux_test.go b/pkg/security/probe/probe_ebpf_test.go similarity index 100% rename from pkg/security/probe/probe_linux_test.go rename to pkg/security/probe/probe_ebpf_test.go diff --git a/pkg/security/probe/probe_epbfless.go b/pkg/security/probe/probe_epbfless.go new file mode 100644 index 0000000000000..039ea352060e5 --- /dev/null +++ b/pkg/security/probe/probe_epbfless.go @@ -0,0 +1,246 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux + +// Package probe holds probe related files +package probe + +import ( + "context" + "errors" + "net" + "path/filepath" + + "google.golang.org/grpc" + + "github.com/DataDog/datadog-agent/pkg/security/config" + "github.com/DataDog/datadog-agent/pkg/security/probe/kfilters" + "github.com/DataDog/datadog-agent/pkg/security/proto/ebpfless" + "github.com/DataDog/datadog-agent/pkg/security/resolvers" + "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" + "github.com/DataDog/datadog-agent/pkg/security/secl/model" + "github.com/DataDog/datadog-agent/pkg/security/secl/rules" + "github.com/DataDog/datadog-agent/pkg/security/seclog" + "github.com/DataDog/datadog-agent/pkg/security/serializers" + "github.com/DataDog/datadog-go/v5/statsd" +) + +// EBPFLessProbe defines an eBPF less probe +type EBPFLessProbe struct { + Resolvers *resolvers.EBPFLessResolvers + + // Constants and configuration + opts Opts + config *config.Config + statsdClient statsd.ClientInterface + + // internals + ebpfless.UnimplementedSyscallMsgStreamServer + server *grpc.Server + seqNum uint64 + probe *Probe + ctx context.Context + cancelFnc context.CancelFunc + fieldHandlers *EBPFLessFieldHandlers +} + +// SendSyscallMsg handles gRPC messages +func (p *EBPFLessProbe) SendSyscallMsg(_ context.Context, syscallMsg *ebpfless.SyscallMsg) (*ebpfless.Response, error) { + if p.seqNum != syscallMsg.SeqNum { + seclog.Errorf("communication out of sync %d vs %d", p.seqNum, syscallMsg.SeqNum) + } + p.seqNum++ + + event := p.probe.zeroEvent() + + switch syscallMsg.Type { + case ebpfless.SyscallType_Exec: + event.Type = uint32(model.ExecEventType) + entry := p.Resolvers.ProcessResolver.AddExecEntry(syscallMsg.PID, syscallMsg.Exec.Filename, syscallMsg.Exec.Args, syscallMsg.Exec.Envs, syscallMsg.ContainerContext.ID) + event.Exec.Process = &entry.Process + case ebpfless.SyscallType_Fork: + event.Type = uint32(model.ForkEventType) + p.Resolvers.ProcessResolver.AddForkEntry(syscallMsg.PID, syscallMsg.Fork.PPID) + case ebpfless.SyscallType_Open: + event.Type = uint32(model.FileOpenEventType) + event.Open.File.PathnameStr = syscallMsg.Open.Filename + event.Open.File.BasenameStr = filepath.Base(syscallMsg.Open.Filename) + event.Open.Flags = syscallMsg.Open.Flags + event.Open.Mode = syscallMsg.Open.Mode + default: + return &ebpfless.Response{}, nil + } + + // container context + event.ContainerContext.ID = syscallMsg.ContainerContext.ID + event.ContainerContext.CreatedAt = syscallMsg.ContainerContext.CreatedAt + event.ContainerContext.Tags = []string{ + "image_name:" + syscallMsg.ContainerContext.Name, + "image_tag:" + syscallMsg.ContainerContext.Tag, + } + + // use ProcessCacheEntry process context as process context + event.ProcessCacheEntry = p.Resolvers.ProcessResolver.Resolve(syscallMsg.PID) + if event.ProcessCacheEntry == nil { + event.ProcessCacheEntry = model.NewPlaceholderProcessCacheEntry(syscallMsg.PID, syscallMsg.PID, false) + } + event.ProcessContext = &event.ProcessCacheEntry.ProcessContext + + p.DispatchEvent(event) + + return &ebpfless.Response{}, nil +} + +// DispatchEvent sends an event to the probe event handler +func (p *EBPFLessProbe) DispatchEvent(event *model.Event) { + traceEvent("Dispatching event %s", func() ([]byte, model.EventType, error) { + eventJSON, err := serializers.MarshalEvent(event) + return eventJSON, event.GetEventType(), err + }) + + // send event to wildcard handlers, like the CWS rule engine, first + p.probe.sendEventToWildcardHandlers(event) + + // send event to specific event handlers, like the event monitor consumers, subsequently + p.probe.sendEventToSpecificEventTypeHandlers(event) +} + +// Init the probe +func (p *EBPFLessProbe) Init() error { + if err := p.Resolvers.Start(p.ctx); err != nil { + return err + } + + return nil +} + +// Stop the probe +func (p *EBPFLessProbe) Stop() { + p.server.GracefulStop() + p.cancelFnc() +} + +// Close the probe +func (p *EBPFLessProbe) Close() error { + return nil +} + +// Start the probe +func (p *EBPFLessProbe) Start() error { + family, address := config.GetFamilyAddress(p.config.RuntimeSecurity.EBPFLessSocket) + + conn, err := net.Listen(family, address) + if err != nil { + return err + } + + go func() { + _ = p.server.Serve(conn) + }() + + seclog.Infof("starting listening for ebpf less events on : %s", p.config.RuntimeSecurity.EBPFLessSocket) + + return nil +} + +// Snapshot the already exsisting entities +func (p *EBPFLessProbe) Snapshot() error { + return nil +} + +// Setup the probe +func (p *EBPFLessProbe) Setup() error { + return nil +} + +// OnNewDiscarder handles discarders +func (p *EBPFLessProbe) OnNewDiscarder(_ *rules.RuleSet, _ *model.Event, _ eval.Field, _ eval.EventType) { +} + +// NewModel returns a new Model +func (p *EBPFLessProbe) NewModel() *model.Model { + return NewEBPFLessModel() +} + +// SendStats send the stats +func (p *EBPFLessProbe) SendStats() error { + return nil +} + +// DumpDiscarders dump the discarders +func (p *EBPFLessProbe) DumpDiscarders() (string, error) { + return "", errors.New("not supported") +} + +// FlushDiscarders flush the discarders +func (p *EBPFLessProbe) FlushDiscarders() error { + return nil +} + +// ApplyRuleSet applies the new ruleset +func (p *EBPFLessProbe) ApplyRuleSet(_ *rules.RuleSet) (*kfilters.ApplyRuleSetReport, error) { + return &kfilters.ApplyRuleSetReport{}, nil +} + +// HandleActions handles the rule actions +func (p *EBPFLessProbe) HandleActions(_ *rules.Rule, _ eval.Event) {} + +// NewEvent returns a new event +func (p *EBPFLessProbe) NewEvent() *model.Event { + return NewEBPFLessEvent(p.fieldHandlers) +} + +// GetFieldHandlers returns the field handlers +func (p *EBPFLessProbe) GetFieldHandlers() model.FieldHandlers { + return p.fieldHandlers +} + +// DumpProcessCache dumps the process cache +func (p *EBPFLessProbe) DumpProcessCache(withArgs bool) (string, error) { + return p.Resolvers.ProcessResolver.Dump(withArgs) +} + +// AddDiscarderPushedCallback add a callback to the list of func that have to be called when a discarder is pushed to kernel +func (p *EBPFLessProbe) AddDiscarderPushedCallback(_ DiscarderPushedCallback) {} + +// GetEventTags returns the event tags +func (p *EBPFLessProbe) GetEventTags(containerID string) []string { + return p.Resolvers.TagsResolver.Resolve(containerID) +} + +// NewEBPFLessProbe returns a new eBPF less probe +func NewEBPFLessProbe(probe *Probe, config *config.Config, opts Opts) (*EBPFLessProbe, error) { + opts.normalize() + + ctx, cancelFnc := context.WithCancel(context.Background()) + + var grpcOpts []grpc.ServerOption + p := &EBPFLessProbe{ + probe: probe, + config: config, + opts: opts, + statsdClient: opts.StatsdClient, + server: grpc.NewServer(grpcOpts...), + ctx: ctx, + cancelFnc: cancelFnc, + } + + ebpfless.RegisterSyscallMsgStreamServer(p.server, p) + + resolversOpts := resolvers.Opts{ + TagsResolver: opts.TagsResolver, + } + + var err error + p.Resolvers, err = resolvers.NewEBPFLessResolvers(config, p.statsdClient, probe.scrubber, resolversOpts) + if err != nil { + return nil, err + } + + p.fieldHandlers = &EBPFLessFieldHandlers{resolvers: p.Resolvers} + + return p, nil +} diff --git a/pkg/security/probe/probe_linux.go b/pkg/security/probe/probe_linux.go index eb2c6c1678a42..6d3dd68d838c9 100644 --- a/pkg/security/probe/probe_linux.go +++ b/pkg/security/probe/probe_linux.go @@ -3,1949 +3,42 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build linux - -// Package probe holds probe related files -package probe - -import ( - "context" - "errors" - "fmt" - "math" - "os" - "path/filepath" - "runtime" - "sync" - "syscall" - "time" - - lib "github.com/cilium/ebpf" - "github.com/hashicorp/go-multierror" - "github.com/moby/sys/mountinfo" - "golang.org/x/exp/slices" - "golang.org/x/sys/unix" - "golang.org/x/time/rate" - "gopkg.in/yaml.v3" - - manager "github.com/DataDog/ebpf-manager" - "github.com/DataDog/ebpf-manager/tracefs" - - "github.com/DataDog/datadog-agent/pkg/collector/corechecks/ebpf/probe/ebpfcheck" - aconfig "github.com/DataDog/datadog-agent/pkg/config" - commonebpf "github.com/DataDog/datadog-agent/pkg/ebpf" - "github.com/DataDog/datadog-agent/pkg/security/config" - "github.com/DataDog/datadog-agent/pkg/security/ebpf" - "github.com/DataDog/datadog-agent/pkg/security/ebpf/kernel" - "github.com/DataDog/datadog-agent/pkg/security/ebpf/probes" - "github.com/DataDog/datadog-agent/pkg/security/events" - "github.com/DataDog/datadog-agent/pkg/security/metrics" - pconfig "github.com/DataDog/datadog-agent/pkg/security/probe/config" - "github.com/DataDog/datadog-agent/pkg/security/probe/constantfetch" - "github.com/DataDog/datadog-agent/pkg/security/probe/erpc" - "github.com/DataDog/datadog-agent/pkg/security/probe/eventstream" - "github.com/DataDog/datadog-agent/pkg/security/probe/eventstream/reorderer" - "github.com/DataDog/datadog-agent/pkg/security/probe/eventstream/ringbuffer" - "github.com/DataDog/datadog-agent/pkg/security/probe/kfilters" - "github.com/DataDog/datadog-agent/pkg/security/probe/managerhelper" - "github.com/DataDog/datadog-agent/pkg/security/resolvers" - "github.com/DataDog/datadog-agent/pkg/security/resolvers/mount" - "github.com/DataDog/datadog-agent/pkg/security/resolvers/netns" - "github.com/DataDog/datadog-agent/pkg/security/resolvers/path" - "github.com/DataDog/datadog-agent/pkg/security/resolvers/process" - "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" - "github.com/DataDog/datadog-agent/pkg/security/secl/model" - "github.com/DataDog/datadog-agent/pkg/security/secl/rules" - "github.com/DataDog/datadog-agent/pkg/security/seclog" - "github.com/DataDog/datadog-agent/pkg/security/security_profile/dump" - "github.com/DataDog/datadog-agent/pkg/security/serializers" - "github.com/DataDog/datadog-agent/pkg/security/utils" - utilkernel "github.com/DataDog/datadog-agent/pkg/util/kernel" - "github.com/DataDog/datadog-agent/pkg/util/log" -) - -// EventStream describes the interface implemented by reordered perf maps or ring buffers -type EventStream interface { - Init(*manager.Manager, *pconfig.Config) error - SetMonitor(eventstream.LostEventCounter) - Start(*sync.WaitGroup) error - Pause() error - Resume() error -} - -var ( - // defaultEventTypes event types used whatever the event handlers or the rules - defaultEventTypes = []eval.EventType{ - model.ForkEventType.String(), - model.ExecEventType.String(), - model.ExitEventType.String(), - } -) - -// PlatformProbe defines a platform probe -type PlatformProbe struct { - // Constants and configuration - Manager *manager.Manager - managerOptions manager.Options - kernelVersion *kernel.Version - - // internals - monitors *Monitors - profileManagers *SecurityProfileManagers - - // Ring - eventStream EventStream - - // ActivityDumps section - activityDumpHandler dump.ActivityDumpHandler - - // Approvers / discarders section - Erpc *erpc.ERPC - erpcRequest *erpc.Request - inodeDiscarders *inodeDiscarders - notifyDiscarderPushedCallbacks []NotifyDiscarderPushedCallback - approvers map[eval.EventType]kfilters.ActiveApprovers - - // Approvers / discarders section - notifyDiscarderPushedCallbacksLock sync.Mutex - - killListMap *lib.Map - supportsBPFSendSignal bool - - isRuntimeDiscarded bool - constantOffsets map[string]uint64 - runtimeCompiled bool - useFentry bool -} - -func (p *Probe) detectKernelVersion() error { - kernelVersion, err := kernel.NewKernelVersion() - if err != nil { - return fmt.Errorf("unable to detect the kernel version: %w", err) - } - p.kernelVersion = kernelVersion - return nil -} - -// GetKernelVersion computes and returns the running kernel version -func (p *Probe) GetKernelVersion() *kernel.Version { - return p.kernelVersion -} - -// UseRingBuffers returns true if eBPF ring buffers are supported and used -func (p *Probe) UseRingBuffers() bool { - return p.Config.Probe.EventStreamUseRingBuffer && p.kernelVersion.HaveRingBuffers() -} - -func (p *Probe) selectFentryMode() { - if !p.Config.Probe.EventStreamUseFentry { - p.useFentry = false - return - } - - supported := p.kernelVersion.HaveFentrySupport() - if !supported { - seclog.Errorf("fentry enabled but not supported, falling back to kprobe mode") - } - p.useFentry = supported -} - -func (p *Probe) sanityChecks() error { - // make sure debugfs is mounted - if _, err := tracefs.Root(); err != nil { - return err - } - - if utilkernel.GetLockdownMode() == utilkernel.Confidentiality { - return errors.New("eBPF not supported in lockdown `confidentiality` mode") - } - - if p.Config.Probe.NetworkEnabled && p.kernelVersion.IsRH7Kernel() { - seclog.Warnf("The network feature of CWS isn't supported on Centos7, setting runtime_security_config.network.enabled to false") - p.Config.Probe.NetworkEnabled = false - } - - return nil -} - -// VerifyOSVersion returns an error if the current kernel version is not supported -func (p *Probe) VerifyOSVersion() error { - if !p.kernelVersion.IsRH7Kernel() && !p.kernelVersion.IsRH8Kernel() && p.kernelVersion.Code < kernel.Kernel4_15 { - return fmt.Errorf("the following kernel is not supported: %s", p.kernelVersion) - } - return nil -} - -// VerifyEnvironment returns an error if the current environment seems to be misconfigured -func (p *Probe) VerifyEnvironment() *multierror.Error { - var err *multierror.Error - if aconfig.IsContainerized() { - if mounted, _ := mountinfo.Mounted("/etc/passwd"); !mounted { - err = multierror.Append(err, errors.New("/etc/passwd doesn't seem to be a mountpoint")) - } - - if mounted, _ := mountinfo.Mounted("/etc/group"); !mounted { - err = multierror.Append(err, errors.New("/etc/group doesn't seem to be a mountpoint")) - } - - if mounted, _ := mountinfo.Mounted(utilkernel.ProcFSRoot()); !mounted { - err = multierror.Append(err, errors.New("/etc/group doesn't seem to be a mountpoint")) - } - - if mounted, _ := mountinfo.Mounted(p.kernelVersion.OsReleasePath); !mounted { - err = multierror.Append(err, fmt.Errorf("%s doesn't seem to be a mountpoint", p.kernelVersion.OsReleasePath)) - } - - securityFSPath := filepath.Join(utilkernel.SysFSRoot(), "kernel/security") - if mounted, _ := mountinfo.Mounted(securityFSPath); !mounted { - err = multierror.Append(err, fmt.Errorf("%s doesn't seem to be a mountpoint", securityFSPath)) - } - - capsEffective, _, capErr := utils.CapEffCapEprm(utils.Getpid()) - if capErr != nil { - err = multierror.Append(capErr, errors.New("failed to get process capabilities")) - } else { - requiredCaps := []string{ - "CAP_SYS_ADMIN", - "CAP_SYS_RESOURCE", - "CAP_SYS_PTRACE", - "CAP_NET_ADMIN", - "CAP_NET_BROADCAST", - "CAP_NET_RAW", - "CAP_IPC_LOCK", - "CAP_CHOWN", - } - - for _, requiredCap := range requiredCaps { - capConst := model.KernelCapabilityConstants[requiredCap] - if capsEffective&capConst == 0 { - err = multierror.Append(err, fmt.Errorf("%s capability is missing", requiredCap)) - } - } - } - } - - return err -} - -// Init initializes the probe -func (p *Probe) Init() error { - p.startTime = time.Now() - - useSyscallWrapper, err := ebpf.IsSyscallWrapperRequired() - if err != nil { - return err - } - - loader := ebpf.NewProbeLoader(p.Config.Probe, useSyscallWrapper, p.UseRingBuffers(), p.useFentry, p.StatsdClient) - defer loader.Close() - - bytecodeReader, runtimeCompiled, err := loader.Load() - if err != nil { - return err - } - defer bytecodeReader.Close() - - p.runtimeCompiled = runtimeCompiled - - if err := p.eventStream.Init(p.Manager, p.Config.Probe); err != nil { - return err - } - - if p.isRuntimeDiscarded { - p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, manager.ConstantEditor{ - Name: "runtime_discarded", - Value: uint64(1), - }) - } - - p.managerOptions.ActivatedProbes = append(p.managerOptions.ActivatedProbes, probes.SnapshotSelectors(p.useFentry)...) - - if err := p.Manager.InitWithOptions(bytecodeReader, p.managerOptions); err != nil { - return fmt.Errorf("failed to init manager: %w", err) - } - - p.inodeDiscarders = newInodeDiscarders(p.Erpc, p.resolvers.DentryResolver) - - if err := p.resolvers.Start(p.ctx); err != nil { - return err - } - - err = p.monitors.Init() - if err != nil { - return err - } - - p.profileManagers, err = NewSecurityProfileManagers(p) - if err != nil { - return err - } - p.profileManagers.AddActivityDumpHandler(p.activityDumpHandler) - - p.eventStream.SetMonitor(p.monitors.eventStreamMonitor) - - p.killListMap, err = managerhelper.Map(p.Manager, "kill_list") - if err != nil { - return err - } - - return nil -} - -// IsRuntimeCompiled returns true if the eBPF programs where successfully runtime compiled -func (p *Probe) IsRuntimeCompiled() bool { - return p.runtimeCompiled -} - -// Setup the runtime security probe -func (p *Probe) Setup() error { - if err := p.Manager.Start(); err != nil { - return err - } - ebpfcheck.AddNameMappings(p.Manager, "cws") - - p.applyDefaultFilterPolicies() - - needRawSyscalls := p.isNeededForActivityDump(model.SyscallsEventType.String()) - - if err := p.updateProbes(defaultEventTypes, needRawSyscalls); err != nil { - return err - } - - p.profileManagers.Start(p.ctx, &p.wg) - - return nil -} - -// Start plays the snapshot data and then start the event stream -func (p *Probe) Start() error { - // Apply rules to the snapshotted data before starting the event stream to avoid concurrency issues - p.playSnapshot() - return p.eventStream.Start(&p.wg) -} - -// playSnapshot plays a snapshot -func (p *Probe) playSnapshot() { - // Get the snapshotted data - var events []*model.Event - - entryToEvent := func(entry *model.ProcessCacheEntry) { - if entry.Source != model.ProcessCacheEntryFromSnapshot { - return - } - entry.Retain() - event := NewEvent(p.fieldHandlers) - event.Type = uint32(model.ExecEventType) - event.TimestampRaw = uint64(time.Now().UnixNano()) - event.ProcessCacheEntry = entry - event.ProcessContext = &entry.ProcessContext - event.Exec.Process = &entry.Process - event.ProcessContext.Process.ContainerID = entry.ContainerID - - if _, err := entry.HasValidLineage(); err != nil { - event.Error = &model.ErrProcessBrokenLineage{Err: err} - } - - events = append(events, event) - } - p.GetResolvers().ProcessResolver.Walk(entryToEvent) - for _, event := range events { - p.DispatchEvent(event) - event.ProcessCacheEntry.Release() - } -} - -func (p *Probe) sendAnomalyDetection(event *model.Event) { - tags := p.GetEventTags(event.ContainerContext.ID) - if service := p.GetService(event); service != "" { - tags = append(tags, "service:"+service) - } - - p.DispatchCustomEvent( - events.NewCustomRule(events.AnomalyDetectionRuleID, events.AnomalyDetectionRuleDesc), - events.NewCustomEventLazy(event.GetEventType(), p.EventMarshallerCtor(event), tags...), - ) -} - -// AddActivityDumpHandler set the probe activity dump handler -func (p *Probe) AddActivityDumpHandler(handler dump.ActivityDumpHandler) { - p.activityDumpHandler = handler -} - -// DispatchEvent sends an event to the probe event handler -func (p *Probe) DispatchEvent(event *model.Event) { - traceEvent("Dispatching event %s", func() ([]byte, model.EventType, error) { - eventJSON, err := serializers.MarshalEvent(event, p.resolvers) - return eventJSON, event.GetEventType(), err - }) - - // filter out event if already present on a profile - if p.Config.RuntimeSecurity.SecurityProfileEnabled { - p.profileManagers.securityProfileManager.LookupEventInProfiles(event) - } - - // send event to wildcard handlers, like the CWS rule engine, first - p.sendEventToWildcardHandlers(event) - - // send event to specific event handlers, like the event monitor consumers, subsequently - p.sendEventToSpecificEventTypeHandlers(event) - - // handle anomaly detections - if event.IsAnomalyDetectionEvent() { - if event.IsKernelSpaceAnomalyDetectionEvent() { - p.profileManagers.securityProfileManager.FillProfileContextFromContainerID(event.FieldHandlers.ResolveContainerID(event, event.ContainerContext), &event.SecurityProfileContext) - } - if p.Config.RuntimeSecurity.AnomalyDetectionEnabled { - p.sendAnomalyDetection(event) - } - } else if event.Error == nil { - // Process event after evaluation because some monitors need the DentryResolver to have been called first. - if p.profileManagers.activityDumpManager != nil { - p.profileManagers.activityDumpManager.ProcessEvent(event) - } - } - p.monitors.ProcessEvent(event) -} - -func (p *Probe) sendEventToWildcardHandlers(event *model.Event) { - for _, handler := range p.fullAccessEventHandlers[model.UnknownEventType] { - handler.HandleEvent(event) - } -} - -func (p *Probe) sendEventToSpecificEventTypeHandlers(event *model.Event) { - for _, handler := range p.eventHandlers[event.GetEventType()] { - handler.HandleEvent(handler.Copy(event)) - } -} - -// DispatchCustomEvent sends a custom event to the probe event handler -func (p *Probe) DispatchCustomEvent(rule *rules.Rule, event *events.CustomEvent) { - traceEvent("Dispatching custom event %s", func() ([]byte, model.EventType, error) { - eventJSON, err := serializers.MarshalCustomEvent(event) - return eventJSON, event.GetEventType(), err - }) - - // send wildcard first - for _, handler := range p.customEventHandlers[model.UnknownEventType] { - handler.HandleCustomEvent(rule, event) - } - - // send specific event - for _, handler := range p.customEventHandlers[event.GetEventType()] { - handler.HandleCustomEvent(rule, event) - } -} - -func traceEvent(fmt string, marshaller func() ([]byte, model.EventType, error)) { - if !seclog.DefaultLogger.IsTracing() { - return - } - - eventJSON, eventType, err := marshaller() - if err != nil { - seclog.DefaultLogger.TraceTagf(eventType, fmt, err) - return - } - - seclog.DefaultLogger.TraceTagf(eventType, fmt, string(eventJSON)) -} - -// SendStats sends statistics about the probe to Datadog -func (p *Probe) SendStats() error { - p.resolvers.TCResolver.SendTCProgramsStats(p.StatsdClient) - - if err := p.profileManagers.SendStats(); err != nil { - return err - } - - return p.monitors.SendStats() -} - -// GetMonitors returns the monitor of the probe -func (p *Probe) GetMonitors() *Monitors { - return p.monitors -} - -// EventMarshallerCtor returns the event marshaller ctor -func (p *Probe) EventMarshallerCtor(event *model.Event) func() events.EventMarshaler { - return func() events.EventMarshaler { - return serializers.NewEventSerializer(event, p.resolvers) - } -} - -func (p *Probe) unmarshalContexts(data []byte, event *model.Event) (int, error) { - read, err := model.UnmarshalBinary(data, &event.PIDContext, &event.SpanContext, event.ContainerContext) - if err != nil { - return 0, err - } - - return read, nil -} - -func eventWithNoProcessContext(eventType model.EventType) bool { - return eventType == model.DNSEventType || eventType == model.LoadModuleEventType || eventType == model.UnloadModuleEventType -} - -func (p *Probe) unmarshalProcessCacheEntry(ev *model.Event, data []byte) (int, error) { - entry := p.resolvers.ProcessResolver.NewProcessCacheEntry(ev.PIDContext) - ev.ProcessCacheEntry = entry - - n, err := entry.Process.UnmarshalBinary(data) - if err != nil { - return n, err - } - entry.Process.ContainerID = ev.ContainerContext.ID - entry.Source = model.ProcessCacheEntryFromEvent - - return n, nil -} - -func (p *Probe) onEventLost(perfMapName string, perEvent map[string]uint64) { - if p.Config.RuntimeSecurity.InternalMonitoringEnabled { - p.DispatchCustomEvent( - NewEventLostWriteEvent(perfMapName, perEvent), - ) - } - - // snapshot traced cgroups if a CgroupTracing event was lost - if p.IsActivityDumpEnabled() && perEvent[model.CgroupTracingEventType.String()] > 0 { - p.profileManagers.SnapshotTracedCgroups() - } -} - -// setProcessContext set the process context, should return false if the event shouldn't be dispatched -func (p *Probe) setProcessContext(eventType model.EventType, event *model.Event) bool { - entry, isResolved := p.fieldHandlers.ResolveProcessCacheEntry(event) - event.ProcessCacheEntry = entry - if event.ProcessCacheEntry == nil { - panic("should always return a process cache entry") - } - - // use ProcessCacheEntry process context as process context - event.ProcessContext = &event.ProcessCacheEntry.ProcessContext - if event.ProcessContext == nil { - panic("should always return a process context") - } - - if process.IsKThread(event.ProcessContext.PPid, event.ProcessContext.Pid) { - return false - } - - if !eventWithNoProcessContext(eventType) { - if !isResolved { - event.Error = &model.ErrNoProcessContext{Err: errors.New("process context not resolved")} - } else if _, err := entry.HasValidLineage(); err != nil { - event.Error = &model.ErrProcessBrokenLineage{Err: err} - p.resolvers.ProcessResolver.CountBrokenLineage() - } - } - - // flush exited process - p.resolvers.ProcessResolver.DequeueExited() - - return true -} - -func (p *Probe) handleEvent(CPU int, data []byte) { - offset := 0 - event := p.zeroEvent() - - dataLen := uint64(len(data)) - - read, err := event.UnmarshalBinary(data) - if err != nil { - seclog.Errorf("failed to decode event: %s", err) - return - } - offset += read - - eventType := event.GetEventType() - if eventType > model.MaxKernelEventType { - seclog.Errorf("unsupported event type %d", eventType) - return - } - - p.monitors.eventStreamMonitor.CountEvent(eventType, event.TimestampRaw, 1, dataLen, eventstream.EventStreamMap, CPU) - - // no need to dispatch events - switch eventType { - case model.MountReleasedEventType: - if _, err = event.MountReleased.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode mount released event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - - // Remove all dentry entries belonging to the mountID - p.resolvers.DentryResolver.DelCacheEntries(event.MountReleased.MountID) - - // Delete new mount point from cache - if err = p.resolvers.MountResolver.Delete(event.MountReleased.MountID); err != nil { - seclog.Tracef("failed to delete mount point %d from cache: %s", event.MountReleased.MountID, err) - } - return - case model.ArgsEnvsEventType: - if _, err = event.ArgsEnvs.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode args envs event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - - p.resolvers.ProcessResolver.UpdateArgsEnvs(&event.ArgsEnvs) - - return - case model.CgroupTracingEventType: - if !p.Config.RuntimeSecurity.ActivityDumpEnabled { - seclog.Errorf("shouldn't receive Cgroup event if activity dumps are disabled") - return - } - - if _, err = event.CgroupTracing.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode cgroup tracing event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - - p.profileManagers.activityDumpManager.HandleCGroupTracingEvent(&event.CgroupTracing) - return - case model.UnshareMountNsEventType: - if _, err = event.UnshareMountNS.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode unshare mnt ns event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - if err := p.handleNewMount(event, &event.UnshareMountNS.Mount); err != nil { - seclog.Debugf("failed to handle new mount from unshare mnt ns event: %s", err) - } - return - } - - read, err = p.unmarshalContexts(data[offset:], event) - if err != nil { - seclog.Errorf("failed to decode event `%s`: %s", eventType, err) - return - } - offset += read - - // save netns handle if applicable - nsPath := utils.NetNSPathFromPid(event.PIDContext.Pid) - _, _ = p.resolvers.NamespaceResolver.SaveNetworkNamespaceHandle(event.PIDContext.NetNS, nsPath) - - if model.GetEventTypeCategory(eventType.String()) == model.NetworkCategory { - if read, err = event.NetworkContext.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode Network Context") - } - offset += read - } - - // handle exec and fork before process context resolution as they modify the process context resolution - switch eventType { - case model.ForkEventType: - if _, err = p.unmarshalProcessCacheEntry(event, data[offset:]); err != nil { - seclog.Errorf("failed to decode fork event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - - if process.IsKThread(event.ProcessCacheEntry.PPid, event.ProcessCacheEntry.Pid) { - return - } - - p.resolvers.ProcessResolver.ApplyBootTime(event.ProcessCacheEntry) - event.ProcessCacheEntry.SetSpan(event.SpanContext.SpanID, event.SpanContext.TraceID) - - p.resolvers.ProcessResolver.AddForkEntry(event.ProcessCacheEntry, event.PIDContext.ExecInode) - case model.ExecEventType: - // unmarshal and fill event.processCacheEntry - if _, err = p.unmarshalProcessCacheEntry(event, data[offset:]); err != nil { - seclog.Errorf("failed to decode exec event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - - if err = p.resolvers.ProcessResolver.ResolveNewProcessCacheEntry(event.ProcessCacheEntry, event.ContainerContext); err != nil { - seclog.Debugf("failed to resolve new process cache entry context for pid %d: %s", event.PIDContext.Pid, err) - - var errResolution *path.ErrPathResolution - if errors.As(err, &errResolution) { - event.SetPathResolutionError(&event.ProcessCacheEntry.FileEvent, err) - } - } else { - p.resolvers.ProcessResolver.AddExecEntry(event.ProcessCacheEntry, event.PIDContext.ExecInode) - } - - event.Exec.Process = &event.ProcessCacheEntry.Process - } - - if !p.setProcessContext(eventType, event) { - return - } - - switch eventType { - case model.FileMountEventType: - if _, err = event.Mount.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode mount event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - if err := p.handleNewMount(event, &event.Mount.Mount); err != nil { - seclog.Debugf("failed to handle new mount from mount event: %s\n", err) - return - } - - // TODO: this should be moved in the resolver itself in order to handle the fallbacks - if event.Mount.GetFSType() == "nsfs" { - nsid := uint32(event.Mount.RootPathKey.Inode) - mountPath, err := p.resolvers.MountResolver.ResolveMountPath(event.Mount.MountID, event.Mount.Device, event.PIDContext.Pid, event.ContainerContext.ID) - if err != nil { - seclog.Debugf("failed to get mount path: %v", err) - } else { - mountNetNSPath := utils.NetNSPathFromPath(mountPath) - _, _ = p.resolvers.NamespaceResolver.SaveNetworkNamespaceHandle(nsid, mountNetNSPath) - } - } - - case model.FileUmountEventType: - if _, err = event.Umount.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode umount event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - - // we can skip this error as this is for the umount only and there is no impact on the filepath resolution - mount, _ := p.resolvers.MountResolver.ResolveMount(event.Umount.MountID, 0, event.PIDContext.Pid, event.ContainerContext.ID) - if mount != nil && mount.GetFSType() == "nsfs" { - nsid := uint32(mount.RootPathKey.Inode) - if namespace := p.resolvers.NamespaceResolver.ResolveNetworkNamespace(nsid); namespace != nil { - p.FlushNetworkNamespace(namespace) - } - } - - case model.FileOpenEventType: - if _, err = event.Open.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode open event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - case model.FileMkdirEventType: - if _, err = event.Mkdir.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode mkdir event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - case model.FileRmdirEventType: - if _, err = event.Rmdir.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode rmdir event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - case model.FileUnlinkEventType: - if _, err = event.Unlink.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode unlink event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - case model.FileRenameEventType: - if _, err = event.Rename.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode rename event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - case model.FileChmodEventType: - if _, err = event.Chmod.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode chmod event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - case model.FileChownEventType: - if _, err = event.Chown.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode chown event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - case model.FileUtimesEventType: - if _, err = event.Utimes.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode utime event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - case model.FileLinkEventType: - if _, err = event.Link.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode link event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - case model.FileSetXAttrEventType: - if _, err = event.SetXAttr.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode setxattr event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - case model.FileRemoveXAttrEventType: - if _, err = event.RemoveXAttr.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode removexattr event: %s (offset %d, len %d)", err, offset, dataLen) - return - } - case model.ExitEventType: - if _, err = event.Exit.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode exit event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - - var exists bool - event.ProcessCacheEntry, exists = p.fieldHandlers.GetProcessCacheEntry(event) - if !exists { - // no need to dispatch an exit event that don't have the corresponding cache entry - return - } - - // Use the event timestamp as exit time - // The local process cache hasn't been updated yet with the exit time when the exit event is first seen - // The pid_cache kernel map has the exit_time but it's only accessed if there's a local miss - event.ProcessCacheEntry.Process.ExitTime = p.fieldHandlers.ResolveEventTime(event) - event.Exit.Process = &event.ProcessCacheEntry.Process - - // update mount pid mapping - p.resolvers.MountResolver.DelPid(event.Exit.Pid) - case model.SetuidEventType: - // the process context may be incorrect, do not modify it - if event.Error != nil { - break - } - - if _, err = event.SetUID.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode setuid event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - defer p.resolvers.ProcessResolver.UpdateUID(event.PIDContext.Pid, event) - case model.SetgidEventType: - // the process context may be incorrect, do not modify it - if event.Error != nil { - break - } - - if _, err = event.SetGID.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode setgid event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - defer p.resolvers.ProcessResolver.UpdateGID(event.PIDContext.Pid, event) - case model.CapsetEventType: - // the process context may be incorrect, do not modify it - if event.Error != nil { - break - } - - if _, err = event.Capset.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode capset event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - defer p.resolvers.ProcessResolver.UpdateCapset(event.PIDContext.Pid, event) - case model.SELinuxEventType: - if _, err = event.SELinux.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode selinux event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - case model.BPFEventType: - if _, err = event.BPF.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode bpf event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - case model.PTraceEventType: - if _, err = event.PTrace.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode ptrace event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - // resolve tracee process context - var pce *model.ProcessCacheEntry - if event.PTrace.PID > 0 { // pid can be 0 for a PTRACE_TRACEME request - pce = p.resolvers.ProcessResolver.Resolve(event.PTrace.PID, event.PTrace.PID, 0, false) - } - if pce == nil { - pce = model.NewPlaceholderProcessCacheEntry(event.PTrace.PID, event.PTrace.PID, false) - } - event.PTrace.Tracee = &pce.ProcessContext - case model.MMapEventType: - if _, err = event.MMap.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode mmap event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - - if event.MMap.Flags&unix.MAP_ANONYMOUS != 0 { - // no need to trigger a dentry resolver, not backed by any file - event.MMap.File.SetPathnameStr("") - event.MMap.File.SetBasenameStr("") - } - case model.MProtectEventType: - if _, err = event.MProtect.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode mprotect event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - case model.LoadModuleEventType: - if _, err = event.LoadModule.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode load_module event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - - if event.LoadModule.LoadedFromMemory { - // no need to trigger a dentry resolver, not backed by any file - event.LoadModule.File.SetPathnameStr("") - event.LoadModule.File.SetBasenameStr("") - } - case model.UnloadModuleEventType: - if _, err = event.UnloadModule.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode unload_module event: %s (offset %d, len %d)", err, offset, len(data)) - } - case model.SignalEventType: - if _, err = event.Signal.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode signal event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - // resolve target process context - var pce *model.ProcessCacheEntry - if event.Signal.PID > 0 { // Linux accepts a kill syscall with both negative and zero pid - pce = p.resolvers.ProcessResolver.Resolve(event.Signal.PID, event.Signal.PID, 0, false) - } - if pce == nil { - pce = model.NewPlaceholderProcessCacheEntry(event.Signal.PID, event.Signal.PID, false) - } - event.Signal.Target = &pce.ProcessContext - case model.SpliceEventType: - if _, err = event.Splice.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode splice event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - case model.NetDeviceEventType: - if _, err = event.NetDevice.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode net_device event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - _ = p.setupNewTCClassifier(event.NetDevice.Device) - case model.VethPairEventType: - if _, err = event.VethPair.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode veth_pair event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - _ = p.setupNewTCClassifier(event.VethPair.PeerDevice) - case model.DNSEventType: - if _, err = event.DNS.UnmarshalBinary(data[offset:]); err != nil { - if errors.Is(err, model.ErrDNSNameMalformatted) { - seclog.Debugf("failed to validate DNS event: %s", event.DNS.Name) - } else if errors.Is(err, model.ErrDNSNamePointerNotSupported) { - seclog.Tracef("failed to decode DNS event: %s (offset %d, len %d)", err, offset, len(data)) - } else { - seclog.Errorf("failed to decode DNS event: %s (offset %d, len %d)", err, offset, len(data)) - } - - return - } - case model.BindEventType: - if _, err = event.Bind.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode bind event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - case model.SyscallsEventType: - if _, err = event.Syscalls.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode syscalls event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - case model.AnomalyDetectionSyscallEventType: - if _, err = event.AnomalyDetectionSyscallEvent.UnmarshalBinary(data[offset:]); err != nil { - seclog.Errorf("failed to decode anomaly detection for syscall event: %s (offset %d, len %d)", err, offset, len(data)) - return - } - } - - // resolve the container context - event.ContainerContext, _ = p.fieldHandlers.ResolveContainerContext(event) - - p.DispatchEvent(event) - - if eventType == model.ExitEventType { - p.resolvers.ProcessResolver.DeleteEntry(event.ProcessContext.Pid, p.fieldHandlers.ResolveEventTime(event)) - } -} - -// AddNewNotifyDiscarderPushedCallback add a callback to the list of func that have to be called when a discarder is pushed to kernel -func (p *Probe) AddNewNotifyDiscarderPushedCallback(cb NotifyDiscarderPushedCallback) { - p.notifyDiscarderPushedCallbacksLock.Lock() - defer p.notifyDiscarderPushedCallbacksLock.Unlock() - - p.notifyDiscarderPushedCallbacks = append(p.notifyDiscarderPushedCallbacks, cb) -} - -// OnNewDiscarder is called when a new discarder is found -func (p *Probe) OnNewDiscarder(rs *rules.RuleSet, ev *model.Event, field eval.Field, eventType eval.EventType) { - // discarders disabled - if !p.Config.Probe.EnableDiscarders { - return - } - - if p.isRuntimeDiscarded { - fakeTime := time.Unix(0, int64(ev.TimestampRaw)) - if !p.discarderRateLimiter.AllowN(fakeTime, 1) { - return - } - } - - seclog.Tracef("New discarder of type %s for field %s", eventType, field) - - if handlers, ok := allDiscarderHandlers[eventType]; ok { - for _, handler := range handlers { - discarderPushed, _ := handler(rs, ev, p, Discarder{Field: field}) - - if discarderPushed { - p.notifyDiscarderPushedCallbacksLock.Lock() - defer p.notifyDiscarderPushedCallbacksLock.Unlock() - for _, cb := range p.notifyDiscarderPushedCallbacks { - cb(eventType, ev, field) - } - } - } - } -} - -// ApplyFilterPolicy is called when a passing policy for an event type is applied -func (p *Probe) ApplyFilterPolicy(eventType eval.EventType, mode kfilters.PolicyMode, flags kfilters.PolicyFlag) error { - seclog.Infof("Setting in-kernel filter policy to `%s` for `%s`", mode, eventType) - table, err := managerhelper.Map(p.Manager, "filter_policy") - if err != nil { - return fmt.Errorf("unable to find policy table: %w", err) - } - - et := config.ParseEvalEventType(eventType) - if et == model.UnknownEventType { - return errors.New("unable to parse the eval event type") - } - - policy := &kfilters.FilterPolicy{ - Mode: mode, - Flags: flags, - } - - return table.Put(ebpf.Uint32MapItem(et), policy) -} - -// SetApprovers applies approvers and removes the unused ones -func (p *Probe) SetApprovers(eventType eval.EventType, approvers rules.Approvers) error { - handler, exists := kfilters.AllApproversHandlers[eventType] - if !exists { - return nil - } - - newApprovers, err := handler(approvers) - if err != nil { - seclog.Errorf("Error while adding approvers fallback in-kernel policy to `%s` for `%s`: %s", kfilters.PolicyModeAccept, eventType, err) - } - - type tag struct { - eventType eval.EventType - approverType string - } - approverAddedMetricCounter := make(map[tag]float64) - - for _, newApprover := range newApprovers { - seclog.Tracef("Applying approver %+v for event type %s", newApprover, eventType) - if err := newApprover.Apply(p.Manager); err != nil { - return err - } - - approverType := getApproverType(newApprover.GetTableName()) - approverAddedMetricCounter[tag{eventType, approverType}]++ - } - - if previousApprovers, exist := p.approvers[eventType]; exist { - previousApprovers.Sub(newApprovers) - for _, previousApprover := range previousApprovers { - seclog.Tracef("Removing previous approver %+v for event type %s", previousApprover, eventType) - if err := previousApprover.Remove(p.Manager); err != nil { - return err - } - - approverType := getApproverType(previousApprover.GetTableName()) - approverAddedMetricCounter[tag{eventType, approverType}]-- - if approverAddedMetricCounter[tag{eventType, approverType}] <= 0 { - delete(approverAddedMetricCounter, tag{eventType, approverType}) - } - } - } - - for tags, count := range approverAddedMetricCounter { - tags := []string{ - fmt.Sprintf("approver_type:%s", tags.approverType), - fmt.Sprintf("event_type:%s", tags.eventType), - } - - if err := p.StatsdClient.Gauge(metrics.MetricApproverAdded, count, tags, 1.0); err != nil { - seclog.Tracef("couldn't set MetricApproverAdded metric: %s", err) - } - } - - p.approvers[eventType] = newApprovers - return nil -} - -func getApproverType(approverTableName string) string { - approverType := "flag" - - if approverTableName == kfilters.BasenameApproverKernelMapName { - approverType = "basename" - } - - return approverType -} - -func (p *Probe) isNeededForActivityDump(eventType eval.EventType) bool { - if p.Config.RuntimeSecurity.ActivityDumpEnabled { - for _, e := range p.profileManagers.GetActivityDumpTracedEventTypes() { - if e.String() == eventType { - return true - } - } - } - return false -} - -func (p *Probe) isNeededForSecurityProfile(eventType eval.EventType) bool { - if p.Config.RuntimeSecurity.SecurityProfileEnabled { - for _, e := range p.Config.RuntimeSecurity.AnomalyDetectionEventTypes { - if e.String() == eventType { - return true - } - } - } - return false -} - -func (p *Probe) validEventTypeForConfig(eventType string) bool { - if eventType == "dns" && !p.Config.Probe.NetworkEnabled { - return false - } - return true -} - -// updateProbes applies the loaded set of rules and returns a report -// of the applied approvers for it. -func (p *Probe) updateProbes(ruleEventTypes []eval.EventType, needRawSyscalls bool) error { - // event types enabled either by event handlers or by rules - eventTypes := append([]eval.EventType{}, defaultEventTypes...) - eventTypes = append(eventTypes, ruleEventTypes...) - for eventType, handlers := range p.eventHandlers { - if len(handlers) == 0 { - continue - } - if slices.Contains(eventTypes, model.EventType(eventType).String()) { - continue - } - if eventType != int(model.UnknownEventType) && eventType != int(model.MaxAllEventType) { - eventTypes = append(eventTypes, model.EventType(eventType).String()) - } - } - - activatedProbes := probes.SnapshotSelectors(p.useFentry) - - // extract probe to activate per the event types - for eventType, selectors := range probes.GetSelectorsPerEventType(p.useFentry) { - if (eventType == "*" || slices.Contains(eventTypes, eventType) || p.isNeededForActivityDump(eventType) || p.isNeededForSecurityProfile(eventType)) && p.validEventTypeForConfig(eventType) { - activatedProbes = append(activatedProbes, selectors...) - } - } - - activatedProbes = append(activatedProbes, p.resolvers.TCResolver.SelectTCProbes()) - - if needRawSyscalls { - activatedProbes = append(activatedProbes, probes.SyscallMonitorSelectors...) - } else { - // ActivityDumps - if p.Config.RuntimeSecurity.ActivityDumpEnabled { - for _, e := range p.profileManagers.GetActivityDumpTracedEventTypes() { - if e == model.SyscallsEventType { - activatedProbes = append(activatedProbes, probes.SyscallMonitorSelectors...) - break - } - } - } - } - - // Print the list of unique probe identification IDs that are registered - var selectedIDs []manager.ProbeIdentificationPair - for _, selector := range activatedProbes { - for _, id := range selector.GetProbesIdentificationPairList() { - var exists bool - for _, selectedID := range selectedIDs { - if selectedID == id { - exists = true - } - } - if !exists { - selectedIDs = append(selectedIDs, id) - seclog.Tracef("probe %s selected", id) - } - } - } - - enabledEventsMap, err := managerhelper.Map(p.Manager, "enabled_events") - if err != nil { - return err - } - - enabledEvents := uint64(0) - for _, eventName := range eventTypes { - if eventName != "*" { - eventType := config.ParseEvalEventType(eventName) - if eventType == model.UnknownEventType { - return fmt.Errorf("unknown event type '%s'", eventName) - } - enabledEvents |= 1 << (eventType - 1) - } - } - - if err := enabledEventsMap.Put(ebpf.ZeroUint32MapItem, enabledEvents); err != nil { - return fmt.Errorf("failed to set enabled events: %w", err) - } - - return p.Manager.UpdateActivatedProbes(activatedProbes) -} - -// GetDiscarders retrieve the discarders -func (p *Probe) GetDiscarders() (*DiscardersDump, error) { - inodeMap, err := managerhelper.Map(p.Manager, "inode_discarders") - if err != nil { - return nil, err - } - - pidMap, err := managerhelper.Map(p.Manager, "pid_discarders") - if err != nil { - return nil, err - } - - statsFB, err := managerhelper.Map(p.Manager, "fb_discarder_stats") - if err != nil { - return nil, err - } - - statsBB, err := managerhelper.Map(p.Manager, "bb_discarder_stats") - if err != nil { - return nil, err - } - - dump, err := dumpDiscarders(p.resolvers.DentryResolver, pidMap, inodeMap, statsFB, statsBB) - if err != nil { - return nil, err - } - return &dump, nil -} - -// DumpDiscarders removes all the discarders -func (p *Probe) DumpDiscarders() (string, error) { - seclog.Debugf("Dumping discarders") - - dump, err := p.GetDiscarders() - if err != nil { - return "", err - } - - fp, err := os.CreateTemp("/tmp", "discarder-dump-") - if err != nil { - return "", err - } - defer fp.Close() - - if err := os.Chmod(fp.Name(), 0400); err != nil { - return "", err - } - - encoder := yaml.NewEncoder(fp) - defer encoder.Close() - - if err := encoder.Encode(dump); err != nil { - return "", err - } - err = fp.Close() - if err != nil { - return "", fmt.Errorf("could not close file [%s]: %w", fp.Name(), err) - } - return fp.Name(), err -} - -// FlushDiscarders invalidates all the discarders -func (p *Probe) FlushDiscarders() error { - seclog.Debugf("Flushing discarders") - return bumpDiscardersRevision(p.Erpc) -} - -// RefreshUserCache refreshes the user cache -func (p *Probe) RefreshUserCache(containerID string) error { - return p.GetResolvers().UserGroupResolver.RefreshCache(containerID) -} - -// Snapshot runs the different snapshot functions of the resolvers that -// require to sync with the current state of the system -func (p *Probe) Snapshot() error { - // the snapshot for the read of a lot of file which can allocate a lot of memory. - defer runtime.GC() - return p.resolvers.Snapshot() -} - -// Stop the probe -func (p *Probe) Stop() { - _ = p.Manager.StopReaders(manager.CleanAll) -} - -// Close the probe -func (p *Probe) Close() error { - // Cancelling the context will stop the reorderer = we won't dequeue events anymore and new events from the - // perf map reader are ignored - p.cancelFnc() - - // we wait until both the reorderer and the monitor are stopped - p.wg.Wait() - - ebpfcheck.RemoveNameMappings(p.Manager) - // Stopping the manager will stop the perf map reader and unload eBPF programs - if err := p.Manager.Stop(manager.CleanAll); err != nil { - return err - } - - // when we reach this point, we do not generate nor consume events anymore, we can close the resolvers - return p.resolvers.Close() -} - -// GetDebugStats returns the debug stats -func (p *Probe) GetDebugStats() map[string]interface{} { - debug := map[string]interface{}{ - "start_time": p.startTime.String(), - } - // TODO(Will): add manager state - return debug -} - -// QueuedNetworkDeviceError is used to indicate that the new network device was queued until its namespace handle is -// resolved. -type QueuedNetworkDeviceError struct { - msg string -} - -func (err QueuedNetworkDeviceError) Error() string { - return err.msg -} - -func (p *Probe) setupNewTCClassifier(device model.NetDevice) error { - // select netns handle - var handle *os.File - var err error - netns := p.resolvers.NamespaceResolver.ResolveNetworkNamespace(device.NetNS) - if netns != nil { - handle, err = netns.GetNamespaceHandleDup() - } - if err != nil { - defer handle.Close() - } - if netns == nil || err != nil || handle == nil { - // queue network device so that a TC classifier can be added later - p.resolvers.NamespaceResolver.QueueNetworkDevice(device) - return QueuedNetworkDeviceError{msg: fmt.Sprintf("device %s is queued until %d is resolved", device.Name, device.NetNS)} - } - err = p.resolvers.TCResolver.SetupNewTCClassifierWithNetNSHandle(device, handle, p.Manager) - if err != nil { - return err - } - if handle != nil { - if err := handle.Close(); err != nil { - return fmt.Errorf("could not close file [%s]: %w", handle.Name(), err) - } - } - return err -} - -// FlushNetworkNamespace removes all references and stops all TC programs in the provided network namespace. This method -// flushes the network namespace in the network namespace resolver as well. -func (p *Probe) FlushNetworkNamespace(namespace *netns.NetworkNamespace) { - p.resolvers.NamespaceResolver.FlushNetworkNamespace(namespace) - - // cleanup internal structures - p.resolvers.TCResolver.FlushNetworkNamespaceID(namespace.ID(), p.Manager) -} - -func (p *Probe) handleNewMount(ev *model.Event, m *model.Mount) error { - // There could be entries of a previous mount_id in the cache for instance, - // runc does the following : it bind mounts itself (using /proc/exe/self), - // opens a file descriptor on the new file with O_CLOEXEC then umount the bind mount using - // MNT_DETACH. It then does an exec syscall, that will cause the fd to be closed. - // Our dentry resolution of the exec event causes the inode/mount_id to be put in cache, - // so we remove all dentry entries belonging to the mountID. - p.resolvers.DentryResolver.DelCacheEntries(m.MountID) - - // Resolve mount point - if err := p.resolvers.PathResolver.SetMountPoint(ev, m); err != nil { - seclog.Debugf("failed to set mount point: %v", err) - return err - } - // Resolve root - if err := p.resolvers.PathResolver.SetMountRoot(ev, m); err != nil { - seclog.Debugf("failed to set mount root: %v", err) - return err - } - - // Insert new mount point in cache, passing it a copy of the mount that we got from the event - if err := p.resolvers.MountResolver.Insert(*m, 0); err != nil { - seclog.Errorf("failed to insert mount event: %v", err) - return err - } - - return nil -} - -func (p *Probe) applyDefaultFilterPolicies() { - if !p.Config.Probe.EnableKernelFilters { - seclog.Warnf("Forcing in-kernel filter policy to `pass`: filtering not enabled") - } - - for eventType := model.FirstEventType; eventType <= model.LastEventType; eventType++ { - var mode kfilters.PolicyMode - - if !p.Config.Probe.EnableKernelFilters { - mode = kfilters.PolicyModeNoFilter - } else if len(p.eventHandlers[eventType]) > 0 { - mode = kfilters.PolicyModeAccept - } else { - mode = kfilters.PolicyModeDeny - } - - if err := p.ApplyFilterPolicy(eventType.String(), mode, math.MaxUint8); err != nil { - seclog.Debugf("unable to apply to filter policy `%s` for `%s`", eventType, mode) - } - } -} - -// ApplyRuleSet setup the probes for the provided set of rules and returns the policy report. -func (p *Probe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.ApplyRuleSetReport, error) { - if p.Opts.SyscallsMonitorEnabled { - if err := p.monitors.syscallsMonitor.Disable(); err != nil { - return nil, err - } - } - - ars, err := kfilters.NewApplyRuleSetReport(p.Config.Probe, rs) - if err != nil { - return nil, err - } - - for eventType, report := range ars.Policies { - if err := p.ApplyFilterPolicy(eventType, report.Mode, report.Flags); err != nil { - return nil, err - } - if err := p.SetApprovers(eventType, report.Approvers); err != nil { - return nil, err - } - } - - needRawSyscalls := p.isNeededForActivityDump(model.SyscallsEventType.String()) - if !needRawSyscalls { - // Add syscall monitor probes if it's either activated or - // there is an 'kill' action in the ruleset - for _, rule := range rs.GetRules() { - for _, action := range rule.Definition.Actions { - if action.Kill != nil { - needRawSyscalls = true - break - } - } - } - } - - if err := p.updateProbes(rs.GetEventTypes(), needRawSyscalls); err != nil { - return nil, fmt.Errorf("failed to select probes: %w", err) - } - - if p.Opts.SyscallsMonitorEnabled { - if err := p.monitors.syscallsMonitor.Flush(); err != nil { - return nil, err - } - if err := p.monitors.syscallsMonitor.Enable(); err != nil { - return nil, err - } - } +// Package probe holds probe related files +package probe - return ars, nil -} +import ( + "github.com/DataDog/datadog-agent/pkg/security/config" +) // NewProbe instantiates a new runtime security agent probe func NewProbe(config *config.Config, opts Opts) (*Probe, error) { opts.normalize() - nerpc, err := erpc.NewERPC() - if err != nil { - return nil, err - } - - ctx, cancel := context.WithCancel(context.Background()) - p := &Probe{ - Opts: opts, - Config: config, - ctx: ctx, - cancelFnc: cancel, - StatsdClient: opts.StatsdClient, - discarderRateLimiter: rate.NewLimiter(rate.Every(time.Second/5), 100), - PlatformProbe: PlatformProbe{ - approvers: make(map[eval.EventType]kfilters.ActiveApprovers), - managerOptions: ebpf.NewDefaultOptions(), - Erpc: nerpc, - erpcRequest: erpc.NewERPCRequest(0), - isRuntimeDiscarded: !opts.DontDiscardRuntime, - }, - } - - if err := p.detectKernelVersion(); err != nil { - // we need the kernel version to start, fail if we can't get it - return nil, err - } - - if err := p.sanityChecks(); err != nil { - return nil, err - } - - if err := p.VerifyOSVersion(); err != nil { - seclog.Warnf("the current kernel isn't officially supported, some features might not work properly: %v", err) - } - - if err := p.VerifyEnvironment(); err != nil { - seclog.Warnf("the current environment may be misconfigured: %v", err) - } - - p.selectFentryMode() - - useRingBuffers := p.UseRingBuffers() - useMmapableMaps := p.kernelVersion.HaveMmapableMaps() - - p.Manager = ebpf.NewRuntimeSecurityManager(useRingBuffers, p.useFentry) - - p.supportsBPFSendSignal = p.kernelVersion.SupportBPFSendSignal() - - p.ensureConfigDefaults() - - p.monitors = NewMonitors(p) - - numCPU, err := utils.NumCPU() - if err != nil { - return nil, fmt.Errorf("failed to parse CPU count: %w", err) - } - - p.managerOptions.MapSpecEditors = probes.AllMapSpecEditors(numCPU, probes.MapSpecEditorOpts{ - TracedCgroupSize: p.Config.RuntimeSecurity.ActivityDumpTracedCgroupsCount, - UseRingBuffers: useRingBuffers, - UseMmapableMaps: useMmapableMaps, - RingBufferSize: uint32(p.Config.Probe.EventStreamBufferSize), - PathResolutionEnabled: p.Opts.PathResolutionEnabled, - SecurityProfileMaxCount: p.Config.RuntimeSecurity.SecurityProfileMaxCount, - }) - - if config.RuntimeSecurity.ActivityDumpEnabled { - for _, e := range config.RuntimeSecurity.ActivityDumpTracedEventTypes { - if e == model.SyscallsEventType { - // Add syscall monitor probes - p.managerOptions.ActivatedProbes = append(p.managerOptions.ActivatedProbes, probes.SyscallMonitorSelectors...) - break - } - } - } - - p.constantOffsets, err = p.GetOffsetConstants() - if err != nil { - seclog.Warnf("constant fetcher failed: %v", err) - return nil, err - } - - p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, constantfetch.CreateConstantEditors(p.constantOffsets)...) - - areCGroupADsEnabled := config.RuntimeSecurity.ActivityDumpTracedCgroupsCount > 0 - - // Add global constant editors - p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, - manager.ConstantEditor{ - Name: "runtime_pid", - Value: uint64(utils.Getpid()), - }, - manager.ConstantEditor{ - Name: "do_fork_input", - Value: getDoForkInput(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "has_usernamespace_first_arg", - Value: getHasUsernamespaceFirstArg(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "ovl_path_in_ovl_inode", - Value: getOvlPathInOvlInode(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "mount_id_offset", - Value: mount.GetMountIDOffset(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "getattr2", - Value: getAttr2(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "vfs_unlink_dentry_position", - Value: mount.GetVFSLinkDentryPosition(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "vfs_mkdir_dentry_position", - Value: mount.GetVFSMKDirDentryPosition(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "vfs_link_target_dentry_position", - Value: mount.GetVFSLinkTargetDentryPosition(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "vfs_setxattr_dentry_position", - Value: mount.GetVFSSetxattrDentryPosition(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "vfs_removexattr_dentry_position", - Value: mount.GetVFSRemovexattrDentryPosition(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "vfs_rename_input_type", - Value: mount.GetVFSRenameInputType(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "check_helper_call_input", - Value: getCheckHelperCallInputType(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "cgroup_activity_dumps_enabled", - Value: utils.BoolTouint64(config.RuntimeSecurity.ActivityDumpEnabled && areCGroupADsEnabled), - }, - manager.ConstantEditor{ - Name: "net_struct_type", - Value: getNetStructType(p.kernelVersion), - }, - manager.ConstantEditor{ - Name: "syscall_monitor_event_period", - Value: uint64(config.RuntimeSecurity.ActivityDumpSyscallMonitorPeriod.Nanoseconds()), - }, - manager.ConstantEditor{ - Name: "send_signal", - Value: utils.BoolTouint64(p.kernelVersion.SupportBPFSendSignal()), - }, - manager.ConstantEditor{ - Name: "anomaly_syscalls", - Value: utils.BoolTouint64(slices.Contains(p.Config.RuntimeSecurity.AnomalyDetectionEventTypes, model.SyscallsEventType)), - }, - manager.ConstantEditor{ - Name: "monitor_syscalls_map_enabled", - Value: utils.BoolTouint64(opts.SyscallsMonitorEnabled), - }, - ) - - p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, DiscarderConstants...) - p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, getCGroupWriteConstants()) - - // if we are using tracepoints to probe syscall exits, i.e. if we are using an old kernel version (< 4.12) - // we need to use raw_syscall tracepoints for exits, as syscall are not trace when running an ia32 userspace - // process - if probes.ShouldUseSyscallExitTracepoints() { - p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, - manager.ConstantEditor{ - Name: "tracepoint_raw_syscall_fallback", - Value: utils.BoolTouint64(true), - }, - ) + Opts: opts, + Config: config, + StatsdClient: opts.StatsdClient, + scrubber: newProcScrubber(config.Probe.CustomSensitiveWords), } - if useRingBuffers { - p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, - manager.ConstantEditor{ - Name: "use_ring_buffer", - Value: utils.BoolTouint64(true), - }, - ) - } - - if p.kernelVersion.HavePIDLinkStruct() { - p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, - manager.ConstantEditor{ - Name: "kernel_has_pid_link_struct", - Value: utils.BoolTouint64(true), - }, - ) - } - - if p.kernelVersion.HaveLegacyPipeInodeInfoStruct() { - p.managerOptions.ConstantEditors = append(p.managerOptions.ConstantEditors, - manager.ConstantEditor{ - Name: "kernel_has_legacy_pipe_inode_info", - Value: utils.BoolTouint64(true), - }, - ) - } - - // tail calls - p.managerOptions.TailCallRouter = probes.AllTailRoutes(p.Config.Probe.ERPCDentryResolutionEnabled, p.Config.Probe.NetworkEnabled, useMmapableMaps, p.useFentry) - if !p.Config.Probe.ERPCDentryResolutionEnabled || useMmapableMaps { - // exclude the programs that use the bpf_probe_write_user helper - p.managerOptions.ExcludedFunctions = probes.AllBPFProbeWriteUserProgramFunctions() - } - - if !p.Config.Probe.NetworkEnabled { - // prevent all TC classifiers from loading - p.managerOptions.ExcludedFunctions = append(p.managerOptions.ExcludedFunctions, probes.GetAllTCProgramFunctions()...) - } - - if p.useFentry { - afBasedExcluder, err := newAvailableFunctionsBasedExcluder() + if opts.EBPFLessEnabled { + pp, err := NewEBPFLessProbe(p, config, opts) if err != nil { return nil, err } - - p.managerOptions.AdditionalExcludedFunctionCollector = afBasedExcluder - } - - p.scrubber = newProcScrubber(config.Probe.CustomSensitiveWords) - - resolversOpts := resolvers.Opts{ - PathResolutionEnabled: opts.PathResolutionEnabled, - TagsResolver: opts.TagsResolver, - UseRingBuffer: useRingBuffers, - TTYFallbackEnabled: opts.TTYFallbackEnabled, - } - p.resolvers, err = resolvers.NewResolvers(config, p.Manager, p.StatsdClient, p.scrubber, p.Erpc, resolversOpts) - if err != nil { - return nil, err - } - - p.fieldHandlers = &FieldHandlers{resolvers: p.resolvers} - - p.event = NewEvent(p.fieldHandlers) - - // be sure to zero the probe event before everything else - p.zeroEvent() - - if useRingBuffers { - p.eventStream = ringbuffer.New(p.handleEvent) - p.managerOptions.SkipRingbufferReaderStartup = map[string]bool{ - eventstream.EventStreamMap: true, - } + p.PlatformProbe = pp } else { - p.eventStream, err = reorderer.NewOrderedPerfMap(p.ctx, p.handleEvent, p.StatsdClient) + pp, err := NewEBPFProbe(p, config, opts) if err != nil { return nil, err } - p.managerOptions.SkipPerfMapReaderStartup = map[string]bool{ - eventstream.EventStreamMap: true, - } - } - - return p, nil -} - -// GetProfileManagers returns the security profile managers -func (p *Probe) GetProfileManagers() *SecurityProfileManagers { - return p.profileManagers -} - -func (p *Probe) ensureConfigDefaults() { - // enable runtime compiled constants on COS by default - if !p.Config.Probe.RuntimeCompiledConstantsIsSet && p.kernelVersion.IsCOSKernel() { - p.Config.Probe.RuntimeCompiledConstantsEnabled = true - } -} - -const ( - netStructHasProcINum uint64 = 0 - netStructHasNS uint64 = 1 -) - -// getNetStructType returns whether the net structure has a namespace attribute -func getNetStructType(kv *kernel.Version) uint64 { - if kv.IsRH7Kernel() { - return netStructHasProcINum - } - return netStructHasNS -} - -const ( - doForkListInput uint64 = iota - doForkStructInput -) - -func getAttr2(kernelVersion *kernel.Version) uint64 { - if kernelVersion.IsRH7Kernel() { - return 1 - } - return 0 -} - -// getDoForkInput returns the expected input type of _do_fork, do_fork and kernel_clone -func getDoForkInput(kernelVersion *kernel.Version) uint64 { - if kernelVersion.Code != 0 && kernelVersion.Code >= kernel.Kernel5_3 { - return doForkStructInput - } - return doForkListInput -} - -func getHasUsernamespaceFirstArg(kernelVersion *kernel.Version) uint64 { - if kernelVersion.Code != 0 && kernelVersion.Code >= kernel.Kernel6_0 { - return 1 - } - return 0 -} - -func getOvlPathInOvlInode(kernelVersion *kernel.Version) uint64 { - // https://github.com/torvalds/linux/commit/0af950f57fefabab628f1963af881e6b9bfe7f38 - if kernelVersion.Code != 0 && kernelVersion.Code >= kernel.Kernel6_5 { - return 2 - } - - // https://github.com/torvalds/linux/commit/ffa5723c6d259b3191f851a50a98d0352b345b39 - // changes a bit how the lower dentry/inode is stored in `ovl_inode`. To check if we - // are in this configuration we first probe the kernel version, then we check for the - // presence of the function introduced in the same patch. - const patchSentinel = "ovl_i_path_real" - - if kernelVersion.Code != 0 && kernelVersion.Code >= kernel.Kernel5_19 { - return 1 - } - - check, err := commonebpf.VerifyKernelFuncs(patchSentinel) - if err != nil { - return 0 - } - - // VerifyKernelFuncs returns the missing functions - if _, ok := check[patchSentinel]; !ok { - return 1 - } - - return 0 -} - -// getCGroupWriteConstants returns the value of the constant used to determine how cgroups should be captured in kernel -// space -func getCGroupWriteConstants() manager.ConstantEditor { - cgroupWriteConst := uint64(1) - kv, err := kernel.NewKernelVersion() - if err == nil { - if kv.IsRH7Kernel() { - cgroupWriteConst = 2 - } - } - - return manager.ConstantEditor{ - Name: "cgroup_write_type", - Value: cgroupWriteConst, - } -} - -// GetOffsetConstants returns the offsets and struct sizes constants -func (p *Probe) GetOffsetConstants() (map[string]uint64, error) { - constantFetcher := constantfetch.ComposeConstantFetchers(constantfetch.GetAvailableConstantFetchers(p.Config.Probe, p.kernelVersion, p.StatsdClient)) - AppendProbeRequestsToFetcher(constantFetcher, p.kernelVersion) - return constantFetcher.FinishAndGetResults() -} - -// GetConstantFetcherStatus returns the status of the constant fetcher associated with this probe -func (p *Probe) GetConstantFetcherStatus() (*constantfetch.ConstantFetcherStatus, error) { - constantFetcher := constantfetch.ComposeConstantFetchers(constantfetch.GetAvailableConstantFetchers(p.Config.Probe, p.kernelVersion, p.StatsdClient)) - AppendProbeRequestsToFetcher(constantFetcher, p.kernelVersion) - return constantFetcher.FinishAndGetStatus() -} - -// AppendProbeRequestsToFetcher returns the offsets and struct sizes constants, from a constant fetcher -func AppendProbeRequestsToFetcher(constantFetcher constantfetch.ConstantFetcher, kv *kernel.Version) { - constantFetcher.AppendSizeofRequest(constantfetch.SizeOfInode, "struct inode", "linux/fs.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameSuperBlockStructSFlags, "struct super_block", "s_flags", "linux/fs.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameSuperBlockStructSMagic, "struct super_block", "s_magic", "linux/fs.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameDentryStructDSB, "struct dentry", "d_sb", "linux/dcache.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameSignalStructStructTTY, "struct signal_struct", "tty", "linux/sched/signal.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameTTYStructStructName, "struct tty_struct", "name", "linux/tty.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameCredStructUID, "struct cred", "uid", "linux/cred.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameLinuxBinprmP, "struct linux_binprm", "p", "linux/binfmts.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameLinuxBinprmArgc, "struct linux_binprm", "argc", "linux/binfmts.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameLinuxBinprmEnvc, "struct linux_binprm", "envc", "linux/binfmts.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameVMAreaStructFlags, "struct vm_area_struct", "vm_flags", "linux/mm_types.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameFileFinode, "struct file", "f_inode", "linux/fs.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameFileFpath, "struct file", "f_path", "linux/fs.h") - if kv.Code >= kernel.Kernel5_3 { - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameKernelCloneArgsExitSignal, "struct kernel_clone_args", "exit_signal", "linux/sched/task.h") - } - - // bpf offsets - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFMapStructID, "struct bpf_map", "id", "linux/bpf.h") - if kv.Code != 0 && (kv.Code >= kernel.Kernel4_15 || kv.IsRH7Kernel()) { - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFMapStructName, "struct bpf_map", "name", "linux/bpf.h") - } - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFMapStructMapType, "struct bpf_map", "map_type", "linux/bpf.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFProgAuxStructID, "struct bpf_prog_aux", "id", "linux/bpf.h") - if kv.Code != 0 && (kv.Code >= kernel.Kernel4_15 || kv.IsRH7Kernel()) { - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFProgAuxStructName, "struct bpf_prog_aux", "name", "linux/bpf.h") - } - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFProgStructTag, "struct bpf_prog", "tag", "linux/filter.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFProgStructAux, "struct bpf_prog", "aux", "linux/filter.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFProgStructType, "struct bpf_prog", "type", "linux/filter.h") - - if kv.Code != 0 && (kv.Code > kernel.Kernel4_16 || kv.IsSuse12Kernel() || kv.IsSuse15Kernel()) { - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameBPFProgStructExpectedAttachType, "struct bpf_prog", "expected_attach_type", "linux/filter.h") - } - // namespace nr offsets - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePIDStructLevel, "struct pid", "level", "linux/pid.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePIDStructNumbers, "struct pid", "numbers", "linux/pid.h") - constantFetcher.AppendSizeofRequest(constantfetch.SizeOfUPID, "struct upid", "linux/pid.h") - if kv.HavePIDLinkStruct() { - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameTaskStructPIDLink, "struct task_struct", "pids", "linux/sched.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePIDLinkStructPID, "struct pid_link", "pid", "linux/pid.h") - } else { - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameTaskStructPID, "struct task_struct", "thread_pid", "linux/sched.h") - } - - // splice event - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePipeInodeInfoStructBufs, "struct pipe_inode_info", "bufs", "linux/pipe_fs_i.h") - if kv.HaveLegacyPipeInodeInfoStruct() { - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePipeInodeInfoStructNrbufs, "struct pipe_inode_info", "nrbufs", "linux/pipe_fs_i.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePipeInodeInfoStructCurbuf, "struct pipe_inode_info", "curbuf", "linux/pipe_fs_i.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePipeInodeInfoStructBuffers, "struct pipe_inode_info", "buffers", "linux/pipe_fs_i.h") - } else { - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePipeInodeInfoStructHead, "struct pipe_inode_info", "head", "linux/pipe_fs_i.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNamePipeInodeInfoStructRingsize, "struct pipe_inode_info", "ring_size", "linux/pipe_fs_i.h") - } - - // network related constants - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameNetDeviceStructIfIndex, "struct net_device", "ifindex", "linux/netdevice.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameSockCommonStructSKCNet, "struct sock_common", "skc_net", "net/sock.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameSockCommonStructSKCFamily, "struct sock_common", "skc_family", "net/sock.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameFlowI4StructSADDR, "struct flowi4", "saddr", "net/flow.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameFlowI4StructULI, "struct flowi4", "uli", "net/flow.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameFlowI6StructSADDR, "struct flowi6", "saddr", "net/flow.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameFlowI6StructULI, "struct flowi6", "uli", "net/flow.h") - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameSocketStructSK, "struct socket", "sk", "linux/net.h") - - // Interpreter constants - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameLinuxBinprmStructFile, "struct linux_binprm", "file", "linux/binfmts.h") - - if !kv.IsRH7Kernel() { - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameNFConnStructCTNet, "struct nf_conn", "ct_net", "net/netfilter/nf_conntrack.h") - } - - if getNetStructType(kv) == netStructHasProcINum { - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameNetStructProcInum, "struct net", "proc_inum", "net/net_namespace.h") - } else { - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameNetStructNS, "struct net", "ns", "net/net_namespace.h") + p.PlatformProbe = pp } - // iouring - if kv.Code != 0 && (kv.Code >= kernel.Kernel5_1) { - constantFetcher.AppendOffsetofRequest(constantfetch.OffsetNameIoKiocbStructCtx, "struct io_kiocb", "ctx", "") - } -} + p.event = p.PlatformProbe.NewEvent() -// HandleActions executes the actions of a triggered rule -func (p *Probe) HandleActions(rule *rules.Rule, event eval.Event) { - ev := event.(*model.Event) - for _, action := range rule.Definition.Actions { - switch { - case action.InternalCallbackDefinition != nil && rule.ID == events.RefreshUserCacheRuleID: - _ = p.RefreshUserCache(ev.ContainerContext.ID) - - case action.Kill != nil: - if pid := ev.ProcessContext.Pid; pid > 1 && pid != utils.Getpid() { - log.Debugf("Requesting signal %s to be sent to %d", action.Kill.Signal, pid) - sig := model.SignalConstants[action.Kill.Signal] + // be sure to zero the probe event before everything else + p.zeroEvent() - var err error - if p.supportsBPFSendSignal { - err = p.killListMap.Put(uint32(pid), uint32(sig)) - } else { - err = syscall.Kill(int(pid), syscall.Signal(sig)) - } - if err != nil { - seclog.Warnf("failed to kill process %d: %s", pid, err) - } - } - } - } + return p, nil } diff --git a/pkg/security/probe/probe_monitor.go b/pkg/security/probe/probe_monitor.go index fc2b8b7c1d343..6953fffecf0e8 100644 --- a/pkg/security/probe/probe_monitor.go +++ b/pkg/security/probe/probe_monitor.go @@ -25,8 +25,8 @@ import ( ) // Monitor regroups all the work we want to do to monitor the probes we pushed in the kernel -type Monitors struct { - probe *Probe +type EBPFMonitors struct { + ebpfProbe *EBPFProbe eventStreamMonitor *eventstream.Monitor runtimeMonitor *runtime.Monitor @@ -36,61 +36,61 @@ type Monitors struct { syscallsMonitor *syscalls.Monitor } -// NewMonitors returns a new instance of a ProbeMonitor -func NewMonitors(p *Probe) *Monitors { - return &Monitors{ - probe: p, +// NewEBPFMonitors returns a new instance of a ProbeMonitor +func NewEBPFMonitors(p *EBPFProbe) *EBPFMonitors { + return &EBPFMonitors{ + ebpfProbe: p, } } // Init initializes the monitor -func (m *Monitors) Init() error { +func (m *EBPFMonitors) Init() error { var err error - p := m.probe + p := m.ebpfProbe // instantiate a new event statistics monitor - m.eventStreamMonitor, err = eventstream.NewEventStreamMonitor(p.Config.Probe, p.Erpc, p.Manager, p.StatsdClient, p.onEventLost, p.UseRingBuffers()) + m.eventStreamMonitor, err = eventstream.NewEventStreamMonitor(p.config.Probe, p.Erpc, p.Manager, p.statsdClient, p.onEventLost, p.UseRingBuffers()) if err != nil { return fmt.Errorf("couldn't create the events statistics monitor: %w", err) } - if p.Config.Probe.RuntimeMonitor { - m.runtimeMonitor = runtime.NewRuntimeMonitor(p.StatsdClient) + if p.config.Probe.RuntimeMonitor { + m.runtimeMonitor = runtime.NewRuntimeMonitor(p.statsdClient) } - m.discarderMonitor, err = discarder.NewDiscarderMonitor(p.Manager, p.StatsdClient) + m.discarderMonitor, err = discarder.NewDiscarderMonitor(p.Manager, p.statsdClient) if err != nil { return fmt.Errorf("couldn't create the discarder monitor: %w", err) } - m.approverMonitor, err = approver.NewApproverMonitor(p.Manager, p.StatsdClient) + m.approverMonitor, err = approver.NewApproverMonitor(p.Manager, p.statsdClient) if err != nil { return fmt.Errorf("couldn't create the approver monitor: %w", err) } - if p.Opts.SyscallsMonitorEnabled { - m.syscallsMonitor, err = syscalls.NewSyscallsMonitor(p.Manager, p.StatsdClient) + if p.opts.SyscallsMonitorEnabled { + m.syscallsMonitor, err = syscalls.NewSyscallsMonitor(p.Manager, p.statsdClient) if err != nil { return fmt.Errorf("couldn't create the approver monitor: %w", err) } } - m.cgroupsMonitor = cgroups.NewCgroupsMonitor(p.StatsdClient, p.resolvers.CGroupResolver) + m.cgroupsMonitor = cgroups.NewCgroupsMonitor(p.statsdClient, p.Resolvers.CGroupResolver) return nil } // GetEventStreamMonitor returns the perf buffer monitor -func (m *Monitors) GetEventStreamMonitor() *eventstream.Monitor { +func (m *EBPFMonitors) GetEventStreamMonitor() *eventstream.Monitor { return m.eventStreamMonitor } // SendStats sends statistics about the probe to Datadog -func (m *Monitors) SendStats() error { +func (m *EBPFMonitors) SendStats() error { // delay between two send in order to reduce the statsd pool presure const delay = time.Second time.Sleep(delay) - if resolvers := m.probe.GetResolvers(); resolvers != nil { + if resolvers := m.ebpfProbe.Resolvers; resolvers != nil { if err := resolvers.ProcessResolver.SendStats(); err != nil { return fmt.Errorf("failed to send process_resolver stats: %w", err) } @@ -122,7 +122,7 @@ func (m *Monitors) SendStats() error { } time.Sleep(delay) - if m.probe.Config.Probe.RuntimeMonitor { + if m.ebpfProbe.config.Probe.RuntimeMonitor { if err := m.runtimeMonitor.SendStats(); err != nil { return fmt.Errorf("failed to send runtime monitor stats: %w", err) } @@ -140,7 +140,7 @@ func (m *Monitors) SendStats() error { return fmt.Errorf("failed to send evaluation set stats: %w", err) } - if m.probe.Opts.SyscallsMonitorEnabled { + if m.ebpfProbe.opts.SyscallsMonitorEnabled { if err := m.syscallsMonitor.SendStats(); err != nil { return fmt.Errorf("failed to send evaluation set stats: %w", err) } @@ -150,8 +150,8 @@ func (m *Monitors) SendStats() error { } // ProcessEvent processes an event through the various monitors and controllers of the probe -func (m *Monitors) ProcessEvent(event *model.Event) { - if !m.probe.Config.RuntimeSecurity.InternalMonitoringEnabled { +func (m *EBPFMonitors) ProcessEvent(event *model.Event) { + if !m.ebpfProbe.config.RuntimeSecurity.InternalMonitoringEnabled { return } @@ -167,22 +167,22 @@ func (m *Monitors) ProcessEvent(event *model.Event) { var pathErr *path.ErrPathResolution if errors.As(event.Error, &pathErr) { - m.probe.DispatchCustomEvent( - NewAbnormalEvent(events.AbnormalPathRuleID, events.AbnormalPathRuleDesc, event, m.probe, pathErr.Err), + m.ebpfProbe.probe.DispatchCustomEvent( + NewAbnormalEvent(events.AbnormalPathRuleID, events.AbnormalPathRuleDesc, event, pathErr.Err), ) } var processContextErr *model.ErrNoProcessContext if errors.As(event.Error, &processContextErr) { - m.probe.DispatchCustomEvent( - NewAbnormalEvent(events.NoProcessContextErrorRuleID, events.NoProcessContextErrorRuleDesc, event, m.probe, event.Error), + m.ebpfProbe.probe.DispatchCustomEvent( + NewAbnormalEvent(events.NoProcessContextErrorRuleID, events.NoProcessContextErrorRuleDesc, event, event.Error), ) } var brokenLineageErr *model.ErrProcessBrokenLineage if errors.As(event.Error, &brokenLineageErr) { - m.probe.DispatchCustomEvent( - NewAbnormalEvent(events.BrokenProcessLineageErrorRuleID, events.BrokenProcessLineageErrorRuleDesc, event, m.probe, event.Error), + m.ebpfProbe.probe.DispatchCustomEvent( + NewAbnormalEvent(events.BrokenProcessLineageErrorRuleID, events.BrokenProcessLineageErrorRuleDesc, event, event.Error), ) } } diff --git a/pkg/security/probe/probe_others.go b/pkg/security/probe/probe_others.go index c5f6ddb2a35c5..f3e14d131eb24 100644 --- a/pkg/security/probe/probe_others.go +++ b/pkg/security/probe/probe_others.go @@ -12,7 +12,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/security/config" "github.com/DataDog/datadog-agent/pkg/security/probe/kfilters" - "github.com/DataDog/datadog-agent/pkg/security/resolvers" "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/secl/rules" @@ -87,11 +86,6 @@ func (p *Probe) StatsPollingInterval() time.Duration { return p.Config.Probe.StatsPollingInterval } -// GetResolvers returns the resolvers of Probe -func (p *Probe) GetResolvers() *resolvers.Resolvers { - return nil -} - // FlushDiscarders invalidates all the discarders func (p *Probe) FlushDiscarders() error { return nil diff --git a/pkg/security/probe/probe_windows.go b/pkg/security/probe/probe_windows.go index 420b873e9bd9e..8286b8355b376 100644 --- a/pkg/security/probe/probe_windows.go +++ b/pkg/security/probe/probe_windows.go @@ -3,17 +3,15 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build windows - // Package probe holds probe related files package probe import ( "context" + "errors" + "sync" "time" - "golang.org/x/time/rate" - "github.com/DataDog/datadog-agent/pkg/process/procutil" "github.com/DataDog/datadog-agent/pkg/security/config" "github.com/DataDog/datadog-agent/pkg/security/probe/kfilters" @@ -22,20 +20,34 @@ import ( "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/secl/rules" + "github.com/DataDog/datadog-agent/pkg/security/serializers" "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/windowsdriver/procmon" + "github.com/DataDog/datadog-go/v5/statsd" ) -type PlatformProbe struct { - pm *procmon.WinProcmon - onStart chan *procmon.ProcessStartNotification - onStop chan *procmon.ProcessStopNotification +// WindowsProbe defines a Windows probe +type WindowsProbe struct { + Resolvers *resolvers.Resolvers + + // Constants and configuration + opts Opts + config *config.Config + statsdClient statsd.ClientInterface + + // internals + ctx context.Context + cancelFnc context.CancelFunc + wg sync.WaitGroup + probe *Probe + fieldHandlers *FieldHandlers + pm *procmon.WinProcmon + onStart chan *procmon.ProcessStartNotification + onStop chan *procmon.ProcessStopNotification } // Init initializes the probe -func (p *Probe) Init() error { - p.startTime = time.Now() - +func (p *WindowsProbe) Init() error { pm, err := procmon.NewWinProcMon(p.onStart, p.onStop) if err != nil { return err @@ -46,17 +58,17 @@ func (p *Probe) Init() error { } // Setup the runtime security probe -func (p *Probe) Setup() error { +func (p *WindowsProbe) Setup() error { return nil } // Stop the probe -func (p *Probe) Stop() { +func (p *WindowsProbe) Stop() { p.pm.Stop() } // Start processing events -func (p *Probe) Start() error { +func (p *WindowsProbe) Start() error { log.Infof("Windows probe started") p.wg.Add(1) @@ -68,7 +80,7 @@ func (p *Probe) Start() error { ) for { - ev := p.zeroEvent() + ev := p.probe.zeroEvent() select { case <-p.ctx.Done(): return @@ -87,7 +99,7 @@ func (p *Probe) Start() error { continue } - pce, err = p.resolvers.ProcessResolver.AddNewEntry(pid, ppid, start.ImageFile, start.CmdLine) + pce, err = p.Resolvers.ProcessResolver.AddNewEntry(pid, ppid, start.ImageFile, start.CmdLine) if err != nil { log.Errorf("error in resolver %v", err) continue @@ -102,8 +114,8 @@ func (p *Probe) Start() error { } log.Infof("Received stop %v", stop) - pce := p.resolvers.ProcessResolver.GetEntry(pid) - defer p.resolvers.ProcessResolver.DeleteEntry(pid, time.Now()) + pce := p.Resolvers.ProcessResolver.GetEntry(pid) + defer p.Resolvers.ProcessResolver.DeleteEntry(pid, time.Now()) ev.Type = uint32(model.ExitEventType) if pce == nil { @@ -128,36 +140,28 @@ func (p *Probe) Start() error { } // DispatchEvent sends an event to the probe event handler -func (p *Probe) DispatchEvent(event *model.Event) { +func (p *WindowsProbe) DispatchEvent(event *model.Event) { + traceEvent("Dispatching event %s", func() ([]byte, model.EventType, error) { + eventJSON, err := serializers.MarshalEvent(event) + return eventJSON, event.GetEventType(), err + }) // send event to wildcard handlers, like the CWS rule engine, first - p.sendEventToWildcardHandlers(event) + p.probe.sendEventToWildcardHandlers(event) // send event to specific event handlers, like the event monitor consumers, subsequently - p.sendEventToSpecificEventTypeHandlers(event) - -} - -func (p *Probe) sendEventToWildcardHandlers(event *model.Event) { - for _, handler := range p.fullAccessEventHandlers[model.UnknownEventType] { - handler.HandleEvent(event) - } -} + p.probe.sendEventToSpecificEventTypeHandlers(event) -func (p *Probe) sendEventToSpecificEventTypeHandlers(event *model.Event) { - for _, handler := range p.eventHandlers[event.GetEventType()] { - handler.HandleEvent(handler.Copy(event)) - } } // Snapshot runs the different snapshot functions of the resolvers that // require to sync with the current state of the system -func (p *Probe) Snapshot() error { - return p.resolvers.Snapshot() +func (p *WindowsProbe) Snapshot() error { + return p.Resolvers.Snapshot() } // Close the probe -func (p *Probe) Close() error { +func (p *WindowsProbe) Close() error { p.pm.Stop() p.cancelFnc() p.wg.Wait() @@ -165,72 +169,110 @@ func (p *Probe) Close() error { } // SendStats sends statistics about the probe to Datadog -func (p *Probe) SendStats() error { - //p.resolvers.TCResolver.SendTCProgramsStats(p.StatsdClient) - // - //return p.monitor.SendStats() +func (p *WindowsProbe) SendStats() error { return nil } -// GetDebugStats returns the debug stats -func (p *Probe) GetDebugStats() map[string]interface{} { - debug := map[string]interface{}{ - "start_time": p.startTime.String(), - } - return debug -} - -// NewProbe instantiates a new runtime security agent probe -func NewProbe(config *config.Config, opts Opts) (*Probe, error) { +// NewWindowsProbe instantiates a new runtime security agent probe +func NewWindowsProbe(probe *Probe, config *config.Config, opts Opts) (*WindowsProbe, error) { opts.normalize() - ctx, cancel := context.WithCancel(context.Background()) - - p := &Probe{ - Opts: opts, - Config: config, - ctx: ctx, - cancelFnc: cancel, - StatsdClient: opts.StatsdClient, - discarderRateLimiter: rate.NewLimiter(rate.Every(time.Second/5), 100), - PlatformProbe: PlatformProbe{ - onStart: make(chan *procmon.ProcessStartNotification), - onStop: make(chan *procmon.ProcessStopNotification), - }, + ctx, cancelFnc := context.WithCancel(context.Background()) + + p := &WindowsProbe{ + probe: probe, + config: config, + opts: opts, + statsdClient: opts.StatsdClient, + ctx: ctx, + cancelFnc: cancelFnc, + onStart: make(chan *procmon.ProcessStartNotification), + onStop: make(chan *procmon.ProcessStopNotification), } - p.scrubber = newProcScrubber(config.Probe.CustomSensitiveWords) + probe.scrubber = newProcScrubber(config.Probe.CustomSensitiveWords) - resolvers, err := resolvers.NewResolvers(config, p.StatsdClient, p.scrubber) + var err error + p.Resolvers, err = resolvers.NewResolvers(config, p.statsdClient, probe.scrubber) if err != nil { return nil, err } - p.resolvers = resolvers - p.fieldHandlers = &FieldHandlers{resolvers: resolvers} - - p.event = NewEvent(p.fieldHandlers) - - // be sure to zero the probe event before everything else - p.zeroEvent() + p.fieldHandlers = &FieldHandlers{resolvers: p.Resolvers} return p, nil } -// OnNewDiscarder is called when a new discarder is found. We currently don't generate discarders on Windows. -func (p *Probe) OnNewDiscarder(rs *rules.RuleSet, ev *model.Event, field eval.Field, eventType eval.EventType) { //nolint:revive // TODO fix revive unused-parameter -} - // ApplyRuleSet setup the probes for the provided set of rules and returns the policy report. -func (p *Probe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.ApplyRuleSetReport, error) { - return kfilters.NewApplyRuleSetReport(p.Config.Probe, rs) +func (p *WindowsProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.ApplyRuleSetReport, error) { + return kfilters.NewApplyRuleSetReport(p.config.Probe, rs) } // FlushDiscarders invalidates all the discarders -func (p *Probe) FlushDiscarders() error { +func (p *WindowsProbe) FlushDiscarders() error { return nil } +// OnNewDiscarder handles discarders +func (p *WindowsProbe) OnNewDiscarder(_ *rules.RuleSet, _ *model.Event, _ eval.Field, _ eval.EventType) { +} + +// NewModel returns a new Model +func (p *WindowsProbe) NewModel() *model.Model { + return NewWindowsModel(p) +} + +// DumpDiscarders dump the discarders +func (p *WindowsProbe) DumpDiscarders() (string, error) { + return "", errors.New("not supported") +} + +// GetFieldHandlers returns the field handlers +func (p *WindowsProbe) GetFieldHandlers() model.FieldHandlers { + return p.fieldHandlers +} + +// DumpProcessCache dumps the process cache +func (p *WindowsProbe) DumpProcessCache(_ bool) (string, error) { + return "", errors.New("not supported") +} + +// NewEvent returns a new event +func (p *WindowsProbe) NewEvent() *model.Event { + return NewWindowsEvent(p.fieldHandlers) +} + // HandleActions executes the actions of a triggered rule -func (p *Probe) HandleActions(_ *rules.Rule, _ eval.Event) { +func (p *WindowsProbe) HandleActions(_ *rules.Rule, _ eval.Event) {} + +// AddDiscarderPushedCallback add a callback to the list of func that have to be called when a discarder is pushed to kernel +func (p *WindowsProbe) AddDiscarderPushedCallback(_ DiscarderPushedCallback) {} + +// GetEventTags returns the event tags +func (p *WindowsProbe) GetEventTags(_ string) []string { + return nil +} + +// NewProbe instantiates a new runtime security agent probe +func NewProbe(config *config.Config, opts Opts) (*Probe, error) { + opts.normalize() + + p := &Probe{ + Opts: opts, + Config: config, + StatsdClient: opts.StatsdClient, + } + + pp, err := NewWindowsProbe(p, config, opts) + if err != nil { + return nil, err + } + p.PlatformProbe = pp + + p.event = p.PlatformProbe.NewEvent() + + // be sure to zero the probe event before everything else + p.zeroEvent() + + return p, nil } diff --git a/pkg/security/probe/security_profile.go b/pkg/security/probe/security_profile.go index 4f2dc65c580ee..a78f04cf4bd60 100644 --- a/pkg/security/probe/security_profile.go +++ b/pkg/security/probe/security_profile.go @@ -30,28 +30,28 @@ type SecurityProfileManagers struct { } // NewSecurityProfileManagers returns a new manager object -func NewSecurityProfileManagers(probe *Probe) (*SecurityProfileManagers, error) { +func NewSecurityProfileManagers(p *EBPFProbe) (*SecurityProfileManagers, error) { managers := SecurityProfileManagers{ - config: probe.Config, + config: p.config, } - if probe.IsActivityDumpEnabled() { - activityDumpManager, err := dump.NewActivityDumpManager(probe.Config, probe.StatsdClient, func() *model.Event { return NewEvent(probe.fieldHandlers) }, probe.resolvers, probe.kernelVersion, probe.Manager) + if p.probe.IsActivityDumpEnabled() { + activityDumpManager, err := dump.NewActivityDumpManager(p.config, p.statsdClient, p.NewEvent, p.Resolvers, p.kernelVersion, p.Manager) if err != nil { return nil, fmt.Errorf("couldn't create the activity dump manager: %w", err) } managers.activityDumpManager = activityDumpManager } - if probe.IsSecurityProfileEnabled() { - securityProfileManager, err := profile.NewSecurityProfileManager(probe.Config, probe.StatsdClient, probe.resolvers, probe.Manager) + if p.probe.IsSecurityProfileEnabled() { + securityProfileManager, err := profile.NewSecurityProfileManager(p.config, p.statsdClient, p.Resolvers, p.Manager) if err != nil { return nil, fmt.Errorf("couldn't create the security profile manager: %w", err) } managers.securityProfileManager = securityProfileManager } - if probe.IsActivityDumpEnabled() && probe.IsSecurityProfileEnabled() { + if p.probe.IsActivityDumpEnabled() && p.probe.IsSecurityProfileEnabled() { managers.activityDumpManager.SetSecurityProfileManager(managers.securityProfileManager) managers.securityProfileManager.SetActivityDumpManager(managers.activityDumpManager) } diff --git a/pkg/security/probe/selftests/tester_linux.go b/pkg/security/probe/selftests/tester_linux.go index b82b501ceaa07..363d2f2fdc452 100644 --- a/pkg/security/probe/selftests/tester_linux.go +++ b/pkg/security/probe/selftests/tester_linux.go @@ -186,14 +186,14 @@ type selfTestEvent struct { } // IsExpectedEvent sends an event to the tester -func (t *SelfTester) IsExpectedEvent(rule *rules.Rule, event eval.Event, p *probe.Probe) bool { +func (t *SelfTester) IsExpectedEvent(rule *rules.Rule, event eval.Event, _ *probe.Probe) bool { if t.waitingForEvent.Load() && rule.Definition.Policy.Source == policySource { ev, ok := event.(*model.Event) if !ok { return true } - s := serializers.NewEventSerializer(ev, p.GetResolvers()) + s := serializers.NewEventSerializer(ev) if s == nil || s.FileEventSerializer == nil { return true } diff --git a/pkg/security/proto/api/README.md b/pkg/security/proto/README.md similarity index 100% rename from pkg/security/proto/api/README.md rename to pkg/security/proto/README.md diff --git a/pkg/security/proto/ebpfless/service.pb.go b/pkg/security/proto/ebpfless/service.pb.go new file mode 100644 index 0000000000000..d63a158862ede --- /dev/null +++ b/pkg/security/proto/ebpfless/service.pb.go @@ -0,0 +1,803 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc +// source: pkg/security/proto/ebpfless/service.proto + +package ebpfless + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + 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) +) + +type SyscallType int32 + +const ( + SyscallType_Unknown SyscallType = 0 + SyscallType_Exec SyscallType = 1 + SyscallType_Fork SyscallType = 2 + SyscallType_Open SyscallType = 3 + SyscallType_Exit SyscallType = 4 + SyscallType_Fcntl SyscallType = 5 +) + +// Enum value maps for SyscallType. +var ( + SyscallType_name = map[int32]string{ + 0: "Unknown", + 1: "Exec", + 2: "Fork", + 3: "Open", + 4: "Exit", + 5: "Fcntl", + } + SyscallType_value = map[string]int32{ + "Unknown": 0, + "Exec": 1, + "Fork": 2, + "Open": 3, + "Exit": 4, + "Fcntl": 5, + } +) + +func (x SyscallType) Enum() *SyscallType { + p := new(SyscallType) + *p = x + return p +} + +func (x SyscallType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SyscallType) Descriptor() protoreflect.EnumDescriptor { + return file_pkg_security_proto_ebpfless_service_proto_enumTypes[0].Descriptor() +} + +func (SyscallType) Type() protoreflect.EnumType { + return &file_pkg_security_proto_ebpfless_service_proto_enumTypes[0] +} + +func (x SyscallType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SyscallType.Descriptor instead. +func (SyscallType) EnumDescriptor() ([]byte, []int) { + return file_pkg_security_proto_ebpfless_service_proto_rawDescGZIP(), []int{0} +} + +type ContainerContext struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"` + Tag string `protobuf:"bytes,3,opt,name=Tag,proto3" json:"Tag,omitempty"` + CreatedAt uint64 `protobuf:"varint,4,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"` +} + +func (x *ContainerContext) Reset() { + *x = ContainerContext{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerContext) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerContext) ProtoMessage() {} + +func (x *ContainerContext) ProtoReflect() protoreflect.Message { + mi := &file_pkg_security_proto_ebpfless_service_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 ContainerContext.ProtoReflect.Descriptor instead. +func (*ContainerContext) Descriptor() ([]byte, []int) { + return file_pkg_security_proto_ebpfless_service_proto_rawDescGZIP(), []int{0} +} + +func (x *ContainerContext) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *ContainerContext) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ContainerContext) GetTag() string { + if x != nil { + return x.Tag + } + return "" +} + +func (x *ContainerContext) GetCreatedAt() uint64 { + if x != nil { + return x.CreatedAt + } + return 0 +} + +type FcntlSyscallMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Fd uint32 `protobuf:"varint,1,opt,name=Fd,proto3" json:"Fd,omitempty"` + Cmd uint32 `protobuf:"varint,2,opt,name=Cmd,proto3" json:"Cmd,omitempty"` +} + +func (x *FcntlSyscallMsg) Reset() { + *x = FcntlSyscallMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FcntlSyscallMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FcntlSyscallMsg) ProtoMessage() {} + +func (x *FcntlSyscallMsg) ProtoReflect() protoreflect.Message { + mi := &file_pkg_security_proto_ebpfless_service_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 FcntlSyscallMsg.ProtoReflect.Descriptor instead. +func (*FcntlSyscallMsg) Descriptor() ([]byte, []int) { + return file_pkg_security_proto_ebpfless_service_proto_rawDescGZIP(), []int{1} +} + +func (x *FcntlSyscallMsg) GetFd() uint32 { + if x != nil { + return x.Fd + } + return 0 +} + +func (x *FcntlSyscallMsg) GetCmd() uint32 { + if x != nil { + return x.Cmd + } + return 0 +} + +type ExecSyscallMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Filename string `protobuf:"bytes,1,opt,name=Filename,proto3" json:"Filename,omitempty"` + Args []string `protobuf:"bytes,2,rep,name=Args,proto3" json:"Args,omitempty"` + Envs []string `protobuf:"bytes,3,rep,name=Envs,proto3" json:"Envs,omitempty"` +} + +func (x *ExecSyscallMsg) Reset() { + *x = ExecSyscallMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecSyscallMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecSyscallMsg) ProtoMessage() {} + +func (x *ExecSyscallMsg) ProtoReflect() protoreflect.Message { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[2] + 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 ExecSyscallMsg.ProtoReflect.Descriptor instead. +func (*ExecSyscallMsg) Descriptor() ([]byte, []int) { + return file_pkg_security_proto_ebpfless_service_proto_rawDescGZIP(), []int{2} +} + +func (x *ExecSyscallMsg) GetFilename() string { + if x != nil { + return x.Filename + } + return "" +} + +func (x *ExecSyscallMsg) GetArgs() []string { + if x != nil { + return x.Args + } + return nil +} + +func (x *ExecSyscallMsg) GetEnvs() []string { + if x != nil { + return x.Envs + } + return nil +} + +type ForkSyscallMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PPID uint32 `protobuf:"varint,1,opt,name=PPID,proto3" json:"PPID,omitempty"` +} + +func (x *ForkSyscallMsg) Reset() { + *x = ForkSyscallMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ForkSyscallMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ForkSyscallMsg) ProtoMessage() {} + +func (x *ForkSyscallMsg) ProtoReflect() protoreflect.Message { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[3] + 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 ForkSyscallMsg.ProtoReflect.Descriptor instead. +func (*ForkSyscallMsg) Descriptor() ([]byte, []int) { + return file_pkg_security_proto_ebpfless_service_proto_rawDescGZIP(), []int{3} +} + +func (x *ForkSyscallMsg) GetPPID() uint32 { + if x != nil { + return x.PPID + } + return 0 +} + +type ExitSyscallMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ExitSyscallMsg) Reset() { + *x = ExitSyscallMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExitSyscallMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExitSyscallMsg) ProtoMessage() {} + +func (x *ExitSyscallMsg) ProtoReflect() protoreflect.Message { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[4] + 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 ExitSyscallMsg.ProtoReflect.Descriptor instead. +func (*ExitSyscallMsg) Descriptor() ([]byte, []int) { + return file_pkg_security_proto_ebpfless_service_proto_rawDescGZIP(), []int{4} +} + +type OpenSyscallMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Filename string `protobuf:"bytes,1,opt,name=Filename,proto3" json:"Filename,omitempty"` + Flags uint32 `protobuf:"varint,2,opt,name=Flags,proto3" json:"Flags,omitempty"` + Mode uint32 `protobuf:"varint,3,opt,name=Mode,proto3" json:"Mode,omitempty"` +} + +func (x *OpenSyscallMsg) Reset() { + *x = OpenSyscallMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OpenSyscallMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OpenSyscallMsg) ProtoMessage() {} + +func (x *OpenSyscallMsg) ProtoReflect() protoreflect.Message { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[5] + 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 OpenSyscallMsg.ProtoReflect.Descriptor instead. +func (*OpenSyscallMsg) Descriptor() ([]byte, []int) { + return file_pkg_security_proto_ebpfless_service_proto_rawDescGZIP(), []int{5} +} + +func (x *OpenSyscallMsg) GetFilename() string { + if x != nil { + return x.Filename + } + return "" +} + +func (x *OpenSyscallMsg) GetFlags() uint32 { + if x != nil { + return x.Flags + } + return 0 +} + +func (x *OpenSyscallMsg) GetMode() uint32 { + if x != nil { + return x.Mode + } + return 0 +} + +type SyscallMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SeqNum uint64 `protobuf:"varint,1,opt,name=SeqNum,proto3" json:"SeqNum,omitempty"` + Type SyscallType `protobuf:"varint,2,opt,name=Type,proto3,enum=ebpfless.SyscallType" json:"Type,omitempty"` + PID uint32 `protobuf:"varint,3,opt,name=PID,proto3" json:"PID,omitempty"` + ContainerContext *ContainerContext `protobuf:"bytes,4,opt,name=ContainerContext,proto3" json:"ContainerContext,omitempty"` + Exec *ExecSyscallMsg `protobuf:"bytes,5,opt,name=Exec,proto3" json:"Exec,omitempty"` + Open *OpenSyscallMsg `protobuf:"bytes,6,opt,name=Open,proto3" json:"Open,omitempty"` + Fork *ForkSyscallMsg `protobuf:"bytes,7,opt,name=Fork,proto3" json:"Fork,omitempty"` + Exit *ExitSyscallMsg `protobuf:"bytes,8,opt,name=Exit,proto3" json:"Exit,omitempty"` + Fcntl *FcntlSyscallMsg `protobuf:"bytes,9,opt,name=Fcntl,proto3" json:"Fcntl,omitempty"` +} + +func (x *SyscallMsg) Reset() { + *x = SyscallMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SyscallMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyscallMsg) ProtoMessage() {} + +func (x *SyscallMsg) ProtoReflect() protoreflect.Message { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[6] + 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 SyscallMsg.ProtoReflect.Descriptor instead. +func (*SyscallMsg) Descriptor() ([]byte, []int) { + return file_pkg_security_proto_ebpfless_service_proto_rawDescGZIP(), []int{6} +} + +func (x *SyscallMsg) GetSeqNum() uint64 { + if x != nil { + return x.SeqNum + } + return 0 +} + +func (x *SyscallMsg) GetType() SyscallType { + if x != nil { + return x.Type + } + return SyscallType_Unknown +} + +func (x *SyscallMsg) GetPID() uint32 { + if x != nil { + return x.PID + } + return 0 +} + +func (x *SyscallMsg) GetContainerContext() *ContainerContext { + if x != nil { + return x.ContainerContext + } + return nil +} + +func (x *SyscallMsg) GetExec() *ExecSyscallMsg { + if x != nil { + return x.Exec + } + return nil +} + +func (x *SyscallMsg) GetOpen() *OpenSyscallMsg { + if x != nil { + return x.Open + } + return nil +} + +func (x *SyscallMsg) GetFork() *ForkSyscallMsg { + if x != nil { + return x.Fork + } + return nil +} + +func (x *SyscallMsg) GetExit() *ExitSyscallMsg { + if x != nil { + return x.Exit + } + return nil +} + +func (x *SyscallMsg) GetFcntl() *FcntlSyscallMsg { + if x != nil { + return x.Fcntl + } + return nil +} + +type Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Response) Reset() { + *x = Response{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Response) ProtoMessage() {} + +func (x *Response) ProtoReflect() protoreflect.Message { + mi := &file_pkg_security_proto_ebpfless_service_proto_msgTypes[7] + 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 Response.ProtoReflect.Descriptor instead. +func (*Response) Descriptor() ([]byte, []int) { + return file_pkg_security_proto_ebpfless_service_proto_rawDescGZIP(), []int{7} +} + +var File_pkg_security_proto_ebpfless_service_proto protoreflect.FileDescriptor + +var file_pkg_security_proto_ebpfless_service_proto_rawDesc = []byte{ + 0x0a, 0x29, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x62, 0x70, 0x66, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x65, 0x62, 0x70, + 0x66, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x66, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x54, 0x61, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x54, 0x61, 0x67, 0x12, + 0x1c, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x33, 0x0a, + 0x0f, 0x46, 0x63, 0x6e, 0x74, 0x6c, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x4d, 0x73, 0x67, + 0x12, 0x0e, 0x0a, 0x02, 0x46, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x46, 0x64, + 0x12, 0x10, 0x0a, 0x03, 0x43, 0x6d, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x43, + 0x6d, 0x64, 0x22, 0x54, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, + 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x41, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, + 0x41, 0x72, 0x67, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x45, 0x6e, 0x76, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x04, 0x45, 0x6e, 0x76, 0x73, 0x22, 0x24, 0x0a, 0x0e, 0x46, 0x6f, 0x72, 0x6b, + 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x50, + 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x50, 0x50, 0x49, 0x44, 0x22, 0x10, + 0x0a, 0x0e, 0x45, 0x78, 0x69, 0x74, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x4d, 0x73, 0x67, + 0x22, 0x56, 0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x4d, + 0x73, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x46, + 0x6c, 0x61, 0x67, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x92, 0x03, 0x0a, 0x0a, 0x53, 0x79, 0x73, + 0x63, 0x61, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x71, 0x4e, 0x75, + 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x71, 0x4e, 0x75, 0x6d, 0x12, + 0x29, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, + 0x65, 0x62, 0x70, 0x66, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x50, 0x49, + 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x50, 0x49, 0x44, 0x12, 0x46, 0x0a, 0x10, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x65, 0x62, 0x70, 0x66, 0x6c, 0x65, 0x73, + 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x52, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x12, 0x2c, 0x0a, 0x04, 0x45, 0x78, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x65, 0x62, 0x70, 0x66, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x45, 0x78, + 0x65, 0x63, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x52, 0x04, 0x45, 0x78, + 0x65, 0x63, 0x12, 0x2c, 0x0a, 0x04, 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x65, 0x62, 0x70, 0x66, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x52, 0x04, 0x4f, 0x70, 0x65, 0x6e, + 0x12, 0x2c, 0x0a, 0x04, 0x46, 0x6f, 0x72, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x65, 0x62, 0x70, 0x66, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x79, + 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x52, 0x04, 0x46, 0x6f, 0x72, 0x6b, 0x12, 0x2c, + 0x0a, 0x04, 0x45, 0x78, 0x69, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x65, + 0x62, 0x70, 0x66, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x45, 0x78, 0x69, 0x74, 0x53, 0x79, 0x73, 0x63, + 0x61, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x52, 0x04, 0x45, 0x78, 0x69, 0x74, 0x12, 0x2f, 0x0a, 0x05, + 0x46, 0x63, 0x6e, 0x74, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x62, + 0x70, 0x66, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x46, 0x63, 0x6e, 0x74, 0x6c, 0x53, 0x79, 0x73, 0x63, + 0x61, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x52, 0x05, 0x46, 0x63, 0x6e, 0x74, 0x6c, 0x22, 0x0a, 0x0a, + 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x4d, 0x0a, 0x0b, 0x53, 0x79, 0x73, + 0x63, 0x61, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, + 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x45, 0x78, 0x65, 0x63, 0x10, 0x01, 0x12, + 0x08, 0x0a, 0x04, 0x46, 0x6f, 0x72, 0x6b, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x70, 0x65, + 0x6e, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x45, 0x78, 0x69, 0x74, 0x10, 0x04, 0x12, 0x09, 0x0a, + 0x05, 0x46, 0x63, 0x6e, 0x74, 0x6c, 0x10, 0x05, 0x32, 0x50, 0x0a, 0x10, 0x53, 0x79, 0x73, 0x63, + 0x61, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x3c, 0x0a, 0x0e, + 0x53, 0x65, 0x6e, 0x64, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x14, + 0x2e, 0x65, 0x62, 0x70, 0x66, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, + 0x6c, 0x4d, 0x73, 0x67, 0x1a, 0x12, 0x2e, 0x65, 0x62, 0x70, 0x66, 0x6c, 0x65, 0x73, 0x73, 0x2e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1d, 0x5a, 0x1b, 0x70, 0x6b, + 0x67, 0x2f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x65, 0x62, 0x70, 0x66, 0x6c, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_pkg_security_proto_ebpfless_service_proto_rawDescOnce sync.Once + file_pkg_security_proto_ebpfless_service_proto_rawDescData = file_pkg_security_proto_ebpfless_service_proto_rawDesc +) + +func file_pkg_security_proto_ebpfless_service_proto_rawDescGZIP() []byte { + file_pkg_security_proto_ebpfless_service_proto_rawDescOnce.Do(func() { + file_pkg_security_proto_ebpfless_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_security_proto_ebpfless_service_proto_rawDescData) + }) + return file_pkg_security_proto_ebpfless_service_proto_rawDescData +} + +var file_pkg_security_proto_ebpfless_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_pkg_security_proto_ebpfless_service_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_pkg_security_proto_ebpfless_service_proto_goTypes = []interface{}{ + (SyscallType)(0), // 0: ebpfless.SyscallType + (*ContainerContext)(nil), // 1: ebpfless.ContainerContext + (*FcntlSyscallMsg)(nil), // 2: ebpfless.FcntlSyscallMsg + (*ExecSyscallMsg)(nil), // 3: ebpfless.ExecSyscallMsg + (*ForkSyscallMsg)(nil), // 4: ebpfless.ForkSyscallMsg + (*ExitSyscallMsg)(nil), // 5: ebpfless.ExitSyscallMsg + (*OpenSyscallMsg)(nil), // 6: ebpfless.OpenSyscallMsg + (*SyscallMsg)(nil), // 7: ebpfless.SyscallMsg + (*Response)(nil), // 8: ebpfless.Response +} +var file_pkg_security_proto_ebpfless_service_proto_depIdxs = []int32{ + 0, // 0: ebpfless.SyscallMsg.Type:type_name -> ebpfless.SyscallType + 1, // 1: ebpfless.SyscallMsg.ContainerContext:type_name -> ebpfless.ContainerContext + 3, // 2: ebpfless.SyscallMsg.Exec:type_name -> ebpfless.ExecSyscallMsg + 6, // 3: ebpfless.SyscallMsg.Open:type_name -> ebpfless.OpenSyscallMsg + 4, // 4: ebpfless.SyscallMsg.Fork:type_name -> ebpfless.ForkSyscallMsg + 5, // 5: ebpfless.SyscallMsg.Exit:type_name -> ebpfless.ExitSyscallMsg + 2, // 6: ebpfless.SyscallMsg.Fcntl:type_name -> ebpfless.FcntlSyscallMsg + 7, // 7: ebpfless.SyscallMsgStream.SendSyscallMsg:input_type -> ebpfless.SyscallMsg + 8, // 8: ebpfless.SyscallMsgStream.SendSyscallMsg:output_type -> ebpfless.Response + 8, // [8:9] is the sub-list for method output_type + 7, // [7:8] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_pkg_security_proto_ebpfless_service_proto_init() } +func file_pkg_security_proto_ebpfless_service_proto_init() { + if File_pkg_security_proto_ebpfless_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_pkg_security_proto_ebpfless_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContainerContext); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_security_proto_ebpfless_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FcntlSyscallMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_security_proto_ebpfless_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExecSyscallMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_security_proto_ebpfless_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ForkSyscallMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_security_proto_ebpfless_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExitSyscallMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_security_proto_ebpfless_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OpenSyscallMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_security_proto_ebpfless_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyscallMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_security_proto_ebpfless_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Response); 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_pkg_security_proto_ebpfless_service_proto_rawDesc, + NumEnums: 1, + NumMessages: 8, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_pkg_security_proto_ebpfless_service_proto_goTypes, + DependencyIndexes: file_pkg_security_proto_ebpfless_service_proto_depIdxs, + EnumInfos: file_pkg_security_proto_ebpfless_service_proto_enumTypes, + MessageInfos: file_pkg_security_proto_ebpfless_service_proto_msgTypes, + }.Build() + File_pkg_security_proto_ebpfless_service_proto = out.File + file_pkg_security_proto_ebpfless_service_proto_rawDesc = nil + file_pkg_security_proto_ebpfless_service_proto_goTypes = nil + file_pkg_security_proto_ebpfless_service_proto_depIdxs = nil +} diff --git a/pkg/security/proto/ebpfless/service.proto b/pkg/security/proto/ebpfless/service.proto new file mode 100644 index 0000000000000..bc9ad365874ef --- /dev/null +++ b/pkg/security/proto/ebpfless/service.proto @@ -0,0 +1,63 @@ +syntax = "proto3"; + +option go_package = "pkg/security/proto/ebpfless"; + +package ebpfless; + +enum SyscallType { + Unknown = 0; + Exec = 1; + Fork = 2; + Open = 3; + Exit = 4; + Fcntl = 5; +} + +message ContainerContext { + string ID = 1; + string Name = 2; + string Tag = 3; + uint64 CreatedAt = 4; +} + +message FcntlSyscallMsg { + uint32 Fd = 1; + uint32 Cmd = 2; +} + +message ExecSyscallMsg { + string Filename = 1; + repeated string Args = 2; + repeated string Envs = 3; +} + +message ForkSyscallMsg { + uint32 PPID = 1; +} + +message ExitSyscallMsg {} + +message OpenSyscallMsg { + string Filename = 1; + uint32 Flags = 2; + uint32 Mode = 3; +} + +message SyscallMsg { + uint64 SeqNum = 1; + SyscallType Type = 2; + uint32 PID = 3; + ContainerContext ContainerContext = 4; + + ExecSyscallMsg Exec = 5; + OpenSyscallMsg Open = 6; + ForkSyscallMsg Fork = 7; + ExitSyscallMsg Exit = 8; + FcntlSyscallMsg Fcntl = 9; +} + +message Response {} + +service SyscallMsgStream { + rpc SendSyscallMsg (SyscallMsg) returns (Response) {} +} \ No newline at end of file diff --git a/pkg/security/proto/ebpfless/service_grpc.pb.go b/pkg/security/proto/ebpfless/service_grpc.pb.go new file mode 100644 index 0000000000000..b8bbc6f3f83e9 --- /dev/null +++ b/pkg/security/proto/ebpfless/service_grpc.pb.go @@ -0,0 +1,109 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v3.21.12 +// source: pkg/security/proto/ebpfless/service.proto + +package ebpfless + +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 + +const ( + SyscallMsgStream_SendSyscallMsg_FullMethodName = "/ebpfless.SyscallMsgStream/SendSyscallMsg" +) + +// SyscallMsgStreamClient is the client API for SyscallMsgStream 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 SyscallMsgStreamClient interface { + SendSyscallMsg(ctx context.Context, in *SyscallMsg, opts ...grpc.CallOption) (*Response, error) +} + +type syscallMsgStreamClient struct { + cc grpc.ClientConnInterface +} + +func NewSyscallMsgStreamClient(cc grpc.ClientConnInterface) SyscallMsgStreamClient { + return &syscallMsgStreamClient{cc} +} + +func (c *syscallMsgStreamClient) SendSyscallMsg(ctx context.Context, in *SyscallMsg, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, SyscallMsgStream_SendSyscallMsg_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SyscallMsgStreamServer is the server API for SyscallMsgStream service. +// All implementations must embed UnimplementedSyscallMsgStreamServer +// for forward compatibility +type SyscallMsgStreamServer interface { + SendSyscallMsg(context.Context, *SyscallMsg) (*Response, error) + mustEmbedUnimplementedSyscallMsgStreamServer() +} + +// UnimplementedSyscallMsgStreamServer must be embedded to have forward compatible implementations. +type UnimplementedSyscallMsgStreamServer struct { +} + +func (UnimplementedSyscallMsgStreamServer) SendSyscallMsg(context.Context, *SyscallMsg) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method SendSyscallMsg not implemented") +} +func (UnimplementedSyscallMsgStreamServer) mustEmbedUnimplementedSyscallMsgStreamServer() {} + +// UnsafeSyscallMsgStreamServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to SyscallMsgStreamServer will +// result in compilation errors. +type UnsafeSyscallMsgStreamServer interface { + mustEmbedUnimplementedSyscallMsgStreamServer() +} + +func RegisterSyscallMsgStreamServer(s grpc.ServiceRegistrar, srv SyscallMsgStreamServer) { + s.RegisterService(&SyscallMsgStream_ServiceDesc, srv) +} + +func _SyscallMsgStream_SendSyscallMsg_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SyscallMsg) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SyscallMsgStreamServer).SendSyscallMsg(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SyscallMsgStream_SendSyscallMsg_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SyscallMsgStreamServer).SendSyscallMsg(ctx, req.(*SyscallMsg)) + } + return interceptor(ctx, in, info, handler) +} + +// SyscallMsgStream_ServiceDesc is the grpc.ServiceDesc for SyscallMsgStream service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var SyscallMsgStream_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "ebpfless.SyscallMsgStream", + HandlerType: (*SyscallMsgStreamServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SendSyscallMsg", + Handler: _SyscallMsgStream_SendSyscallMsg_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "pkg/security/proto/ebpfless/service.proto", +} diff --git a/pkg/security/proto/ebpfless/service_vtproto.pb.go b/pkg/security/proto/ebpfless/service_vtproto.pb.go new file mode 100644 index 0000000000000..6b92a888fac45 --- /dev/null +++ b/pkg/security/proto/ebpfless/service_vtproto.pb.go @@ -0,0 +1,1726 @@ +// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. +// protoc-gen-go-vtproto version: v0.5.0 +// source: pkg/security/proto/ebpfless/service.proto + +package ebpfless + +import ( + fmt "fmt" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" + bits "math/bits" +) + +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) +) + +func (m *ContainerContext) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ContainerContext) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ContainerContext) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.CreatedAt != 0 { + i = encodeVarint(dAtA, i, uint64(m.CreatedAt)) + i-- + dAtA[i] = 0x20 + } + if len(m.Tag) > 0 { + i -= len(m.Tag) + copy(dAtA[i:], m.Tag) + i = encodeVarint(dAtA, i, uint64(len(m.Tag))) + i-- + dAtA[i] = 0x1a + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarint(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FcntlSyscallMsg) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FcntlSyscallMsg) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *FcntlSyscallMsg) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Cmd != 0 { + i = encodeVarint(dAtA, i, uint64(m.Cmd)) + i-- + dAtA[i] = 0x10 + } + if m.Fd != 0 { + i = encodeVarint(dAtA, i, uint64(m.Fd)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ExecSyscallMsg) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecSyscallMsg) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ExecSyscallMsg) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Envs) > 0 { + for iNdEx := len(m.Envs) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Envs[iNdEx]) + copy(dAtA[i:], m.Envs[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.Envs[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Filename) > 0 { + i -= len(m.Filename) + copy(dAtA[i:], m.Filename) + i = encodeVarint(dAtA, i, uint64(len(m.Filename))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ForkSyscallMsg) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ForkSyscallMsg) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ForkSyscallMsg) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.PPID != 0 { + i = encodeVarint(dAtA, i, uint64(m.PPID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ExitSyscallMsg) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExitSyscallMsg) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ExitSyscallMsg) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + return len(dAtA) - i, nil +} + +func (m *OpenSyscallMsg) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *OpenSyscallMsg) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *OpenSyscallMsg) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Mode != 0 { + i = encodeVarint(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x18 + } + if m.Flags != 0 { + i = encodeVarint(dAtA, i, uint64(m.Flags)) + i-- + dAtA[i] = 0x10 + } + if len(m.Filename) > 0 { + i -= len(m.Filename) + copy(dAtA[i:], m.Filename) + i = encodeVarint(dAtA, i, uint64(len(m.Filename))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SyscallMsg) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SyscallMsg) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SyscallMsg) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Fcntl != nil { + size, err := m.Fcntl.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x4a + } + if m.Exit != nil { + size, err := m.Exit.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x42 + } + if m.Fork != nil { + size, err := m.Fork.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x3a + } + if m.Open != nil { + size, err := m.Open.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x32 + } + if m.Exec != nil { + size, err := m.Exec.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a + } + if m.ContainerContext != nil { + size, err := m.ContainerContext.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x22 + } + if m.PID != 0 { + i = encodeVarint(dAtA, i, uint64(m.PID)) + i-- + dAtA[i] = 0x18 + } + if m.Type != 0 { + i = encodeVarint(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x10 + } + if m.SeqNum != 0 { + i = encodeVarint(dAtA, i, uint64(m.SeqNum)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Response) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Response) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Response) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + return len(dAtA) - i, nil +} + +func encodeVarint(dAtA []byte, offset int, v uint64) int { + offset -= sov(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ContainerContext) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + l = len(m.Tag) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.CreatedAt != 0 { + n += 1 + sov(uint64(m.CreatedAt)) + } + n += len(m.unknownFields) + return n +} + +func (m *FcntlSyscallMsg) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Fd != 0 { + n += 1 + sov(uint64(m.Fd)) + } + if m.Cmd != 0 { + n += 1 + sov(uint64(m.Cmd)) + } + n += len(m.unknownFields) + return n +} + +func (m *ExecSyscallMsg) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Filename) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if len(m.Args) > 0 { + for _, s := range m.Args { + l = len(s) + n += 1 + l + sov(uint64(l)) + } + } + if len(m.Envs) > 0 { + for _, s := range m.Envs { + l = len(s) + n += 1 + l + sov(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *ForkSyscallMsg) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PPID != 0 { + n += 1 + sov(uint64(m.PPID)) + } + n += len(m.unknownFields) + return n +} + +func (m *ExitSyscallMsg) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += len(m.unknownFields) + return n +} + +func (m *OpenSyscallMsg) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Filename) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.Flags != 0 { + n += 1 + sov(uint64(m.Flags)) + } + if m.Mode != 0 { + n += 1 + sov(uint64(m.Mode)) + } + n += len(m.unknownFields) + return n +} + +func (m *SyscallMsg) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SeqNum != 0 { + n += 1 + sov(uint64(m.SeqNum)) + } + if m.Type != 0 { + n += 1 + sov(uint64(m.Type)) + } + if m.PID != 0 { + n += 1 + sov(uint64(m.PID)) + } + if m.ContainerContext != nil { + l = m.ContainerContext.SizeVT() + n += 1 + l + sov(uint64(l)) + } + if m.Exec != nil { + l = m.Exec.SizeVT() + n += 1 + l + sov(uint64(l)) + } + if m.Open != nil { + l = m.Open.SizeVT() + n += 1 + l + sov(uint64(l)) + } + if m.Fork != nil { + l = m.Fork.SizeVT() + n += 1 + l + sov(uint64(l)) + } + if m.Exit != nil { + l = m.Exit.SizeVT() + n += 1 + l + sov(uint64(l)) + } + if m.Fcntl != nil { + l = m.Fcntl.SizeVT() + n += 1 + l + sov(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Response) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += len(m.unknownFields) + return n +} + +func sov(x uint64) (n int) { + return (bits.Len64(x|1) + 6) / 7 +} +func soz(x uint64) (n int) { + return sov(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ContainerContext) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ContainerContext: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ContainerContext: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tag", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tag = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CreatedAt", wireType) + } + m.CreatedAt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CreatedAt |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FcntlSyscallMsg) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FcntlSyscallMsg: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FcntlSyscallMsg: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Fd", wireType) + } + m.Fd = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Fd |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cmd", wireType) + } + m.Cmd = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Cmd |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecSyscallMsg) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecSyscallMsg: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecSyscallMsg: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filename", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Filename = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Envs", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Envs = append(m.Envs, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ForkSyscallMsg) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ForkSyscallMsg: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ForkSyscallMsg: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PPID", wireType) + } + m.PPID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PPID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExitSyscallMsg) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExitSyscallMsg: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExitSyscallMsg: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *OpenSyscallMsg) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: OpenSyscallMsg: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: OpenSyscallMsg: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filename", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Filename = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Flags", wireType) + } + m.Flags = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Flags |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + m.Mode = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Mode |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SyscallMsg) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SyscallMsg: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SyscallMsg: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SeqNum", wireType) + } + m.SeqNum = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SeqNum |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Type |= SyscallType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PID", wireType) + } + m.PID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContainerContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ContainerContext == nil { + m.ContainerContext = &ContainerContext{} + } + if err := m.ContainerContext.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Exec", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Exec == nil { + m.Exec = &ExecSyscallMsg{} + } + if err := m.Exec.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Open", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Open == nil { + m.Open = &OpenSyscallMsg{} + } + if err := m.Open.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Fork", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Fork == nil { + m.Fork = &ForkSyscallMsg{} + } + if err := m.Fork.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Exit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Exit == nil { + m.Exit = &ExitSyscallMsg{} + } + if err := m.Exit.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Fcntl", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Fcntl == nil { + m.Fcntl = &FcntlSyscallMsg{} + } + if err := m.Fcntl.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Response) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Response: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Response: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} + +func skip(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflow + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflow + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflow + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLength + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroup + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLength + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLength = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflow = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroup = fmt.Errorf("proto: unexpected end of group") +) diff --git a/pkg/security/proto/ebpfless/vt_grpc.go b/pkg/security/proto/ebpfless/vt_grpc.go new file mode 100644 index 0000000000000..185e428b27d0f --- /dev/null +++ b/pkg/security/proto/ebpfless/vt_grpc.go @@ -0,0 +1,72 @@ +// Copyright 2021 The Vitess Authors. +// 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. + +// This implementation is based on +// https://github.com/vitessio/vitess/blob/main/go/vt/servenv/grpc_codec.go + +// Package ebpfless holds api related files +package ebpfless + +import ( + fmt "fmt" + + "google.golang.org/grpc/encoding" + _ "google.golang.org/grpc/encoding/proto" // for default proto registration purposes + "google.golang.org/protobuf/proto" +) + +// VTProtoCodecName represents the name of the vtproto codec, using vtproto instead of the default marshalling functions +const VTProtoCodecName = "vtproto" + +// maybeVTCodec represents a codec able to encode and decode vt enabled proto messages +type maybeVTCodec struct{} + +type vtprotoMessage interface { + MarshalVT() ([]byte, error) + UnmarshalVT([]byte) error +} + +// Marshal encodes the protobuf message to a byte array +func (maybeVTCodec) Marshal(v interface{}) ([]byte, error) { + vt, ok := v.(vtprotoMessage) + if ok { + return vt.MarshalVT() + } + + msg, ok := v.(proto.Message) + if !ok { + return nil, fmt.Errorf("failed to marshal, message is %T, want proto.Message", v) + } + return proto.Marshal(msg) +} + +// Unmarshal decodes the byte array to the provided value +func (maybeVTCodec) Unmarshal(data []byte, v interface{}) error { + vt, ok := v.(vtprotoMessage) + if ok { + return vt.UnmarshalVT(data) + } + + msg, ok := v.(proto.Message) + if !ok { + return fmt.Errorf("failed to unmarshal, message is %T, want proto.Message", v) + } + return proto.Unmarshal(data, msg) +} + +// Name returns the name of the codec +func (maybeVTCodec) Name() string { + return VTProtoCodecName +} + +func init() { + encoding.RegisterCodec(maybeVTCodec{}) +} diff --git a/pkg/security/resolvers/opts_linux.go b/pkg/security/resolvers/opts_linux.go new file mode 100644 index 0000000000000..e2ee9890e80fe --- /dev/null +++ b/pkg/security/resolvers/opts_linux.go @@ -0,0 +1,17 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package resolvers holds resolvers related files +package resolvers + +import "github.com/DataDog/datadog-agent/pkg/security/resolvers/tags" + +// Opts defines common options +type Opts struct { + PathResolutionEnabled bool + TagsResolver tags.Resolver + UseRingBuffer bool + TTYFallbackEnabled bool +} diff --git a/pkg/security/resolvers/process/opts_linux.go b/pkg/security/resolvers/process/opts_linux.go new file mode 100644 index 0000000000000..ec28641c37ff3 --- /dev/null +++ b/pkg/security/resolvers/process/opts_linux.go @@ -0,0 +1,34 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package process holds process related files +package process + +// ResolverOpts options of resolver +type ResolverOpts struct { + ttyFallbackEnabled bool + envsWithValue map[string]bool +} + +// WithEnvsValue specifies envs with value +func (o *ResolverOpts) WithEnvsValue(envsWithValue []string) *ResolverOpts { + for _, envVar := range envsWithValue { + o.envsWithValue[envVar] = true + } + return o +} + +// WithTTYFallbackEnabled enables the TTY fallback +func (o *ResolverOpts) WithTTYFallbackEnabled() *ResolverOpts { + o.ttyFallbackEnabled = true + return o +} + +// NewResolverOpts returns a new set of process resolver options +func NewResolverOpts() *ResolverOpts { + return &ResolverOpts{ + envsWithValue: make(map[string]bool), + } +} diff --git a/pkg/security/resolvers/process/resolver_others.go b/pkg/security/resolvers/process/opts_windows.go similarity index 58% rename from pkg/security/resolvers/process/resolver_others.go rename to pkg/security/resolvers/process/opts_windows.go index 3b03be00ae0a7..5fd2847199772 100644 --- a/pkg/security/resolvers/process/resolver_others.go +++ b/pkg/security/resolvers/process/opts_windows.go @@ -3,15 +3,15 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build !linux && !windows - // Package process holds process related files package process -import "go.uber.org/atomic" +// ResolverOpts options of resolver +type ResolverOpts struct { + envsWithValue map[string]bool +} -// Resolver defines a resolver -type Resolver struct { - // stats - cacheSize *atomic.Int64 +// NewResolverOpts returns a new set of process resolver options +func NewResolverOpts() ResolverOpts { + return ResolverOpts{} } diff --git a/pkg/security/resolvers/process/resolver.go b/pkg/security/resolvers/process/resolver.go index 62461ac051038..ec64299312fea 100644 --- a/pkg/security/resolvers/process/resolver.go +++ b/pkg/security/resolvers/process/resolver.go @@ -29,7 +29,7 @@ func (p *Pool) Put(pce *model.ProcessCacheEntry) { } // NewProcessCacheEntryPool returns a new Pool -func NewProcessCacheEntryPool(p *Resolver) *Pool { +func NewProcessCacheEntryPool(onRelease func()) *Pool { pcep := Pool{pool: &sync.Pool{}} pcep.pool.New = func() interface{} { @@ -38,7 +38,7 @@ func NewProcessCacheEntryPool(p *Resolver) *Pool { pce.Ancestor.Release() } - p.cacheSize.Dec() + onRelease() pcep.Put(pce) }) diff --git a/pkg/security/resolvers/process/resolver_linux.go b/pkg/security/resolvers/process/resolver_ebpf.go similarity index 87% rename from pkg/security/resolvers/process/resolver_linux.go rename to pkg/security/resolvers/process/resolver_ebpf.go index 1eabc9ffcce97..08cb4439eec0d 100644 --- a/pkg/security/resolvers/process/resolver_linux.go +++ b/pkg/security/resolvers/process/resolver_ebpf.go @@ -56,14 +56,8 @@ const ( procFallbackLimiterPeriod = 30 * time.Second // proc fallback period by pid ) -// ResolverOpts options of resolver -type ResolverOpts struct { - ttyFallbackEnabled bool - envsWithValue map[string]bool -} - -// Resolver resolved process context -type Resolver struct { +// EBPFResolver resolved process context +type EBPFResolver struct { sync.RWMutex state *atomic.Int64 @@ -113,7 +107,7 @@ type Resolver struct { } // DequeueExited dequeue exited process -func (p *Resolver) DequeueExited() { +func (p *EBPFResolver) DequeueExited() { p.Lock() defer p.Unlock() @@ -142,7 +136,7 @@ func (p *Resolver) DequeueExited() { } // NewProcessCacheEntry returns a new process cache entry -func (p *Resolver) NewProcessCacheEntry(pidContext model.PIDContext) *model.ProcessCacheEntry { +func (p *EBPFResolver) NewProcessCacheEntry(pidContext model.PIDContext) *model.ProcessCacheEntry { entry := p.processCacheEntryPool.Get() entry.PIDContext = pidContext entry.Cookie = utils.NewCookie() @@ -150,12 +144,12 @@ func (p *Resolver) NewProcessCacheEntry(pidContext model.PIDContext) *model.Proc } // CountBrokenLineage increments the counter of broken lineage -func (p *Resolver) CountBrokenLineage() { +func (p *EBPFResolver) CountBrokenLineage() { p.brokenLineage.Inc() } // SendStats sends process resolver metrics -func (p *Resolver) SendStats() error { +func (p *EBPFResolver) SendStats() error { if err := p.statsdClient.Gauge(metrics.MetricProcessResolverCacheSize, p.getCacheSize(), []string{}, 1.0); err != nil { return fmt.Errorf("failed to send process_resolver cache_size metric: %w", err) } @@ -285,7 +279,7 @@ func (e *argsEnvsCacheEntry) extend(event *model.ArgsEnvsEvent) { } // UpdateArgsEnvs updates arguments or environment variables of the given id -func (p *Resolver) UpdateArgsEnvs(event *model.ArgsEnvsEvent) { +func (p *EBPFResolver) UpdateArgsEnvs(event *model.ArgsEnvsEvent) { if list, found := p.argsEnvsCache.Get(event.ID); found { list.extend(event) } else { @@ -294,7 +288,7 @@ func (p *Resolver) UpdateArgsEnvs(event *model.ArgsEnvsEvent) { } // AddForkEntry adds an entry to the local cache and returns the newly created entry -func (p *Resolver) AddForkEntry(entry *model.ProcessCacheEntry, inode uint64) { +func (p *EBPFResolver) AddForkEntry(entry *model.ProcessCacheEntry, inode uint64) { p.Lock() defer p.Unlock() @@ -302,7 +296,7 @@ func (p *Resolver) AddForkEntry(entry *model.ProcessCacheEntry, inode uint64) { } // AddExecEntry adds an entry to the local cache and returns the newly created entry -func (p *Resolver) AddExecEntry(entry *model.ProcessCacheEntry, inode uint64) { +func (p *EBPFResolver) AddExecEntry(entry *model.ProcessCacheEntry, inode uint64) { p.Lock() defer p.Unlock() @@ -310,7 +304,7 @@ func (p *Resolver) AddExecEntry(entry *model.ProcessCacheEntry, inode uint64) { } // enrichEventFromProc uses /proc to enrich a ProcessCacheEntry with additional metadata -func (p *Resolver) enrichEventFromProc(entry *model.ProcessCacheEntry, proc *process.Process, filledProc *utils.FilledProcess) error { +func (p *EBPFResolver) enrichEventFromProc(entry *model.ProcessCacheEntry, proc *process.Process, filledProc *utils.FilledProcess) error { // the provided process is a kernel process if its virtual memory size is null if filledProc.MemInfo.VMS == 0 { return fmt.Errorf("cannot snapshot kernel threads") @@ -347,7 +341,7 @@ func (p *Resolver) enrichEventFromProc(entry *model.ProcessCacheEntry, proc *pro if entry.FileEvent.IsFileless() { entry.FileEvent.Filesystem = model.TmpFS } else { - // resolve container path with the MountResolver + // resolve container path with the MountEBPFResolver entry.FileEvent.Filesystem, err = p.mountResolver.ResolveFilesystem(entry.Process.FileEvent.MountID, entry.Process.FileEvent.Device, entry.Process.Pid, string(containerID)) if err != nil { seclog.Debugf("snapshot failed for mount %d with pid %d : couldn't get the filesystem: %s", entry.Process.FileEvent.MountID, proc.Pid, err) @@ -434,7 +428,7 @@ func (p *Resolver) enrichEventFromProc(entry *model.ProcessCacheEntry, proc *pro } // retrieveExecFileFields fetches inode metadata from kernel space -func (p *Resolver) retrieveExecFileFields(procExecPath string) (*model.FileFields, error) { +func (p *EBPFResolver) retrieveExecFileFields(procExecPath string) (*model.FileFields, error) { fi, err := os.Stat(procExecPath) if err != nil { return nil, fmt.Errorf("snapshot failed for `%s`: couldn't stat binary: %w", procExecPath, err) @@ -465,7 +459,7 @@ func (p *Resolver) retrieveExecFileFields(procExecPath string) (*model.FileField return &fileFields, nil } -func (p *Resolver) insertEntry(entry, prev *model.ProcessCacheEntry, source uint64) { +func (p *EBPFResolver) insertEntry(entry, prev *model.ProcessCacheEntry, source uint64) { entry.Source = source p.entryCache[entry.Pid] = entry entry.Retain() @@ -491,7 +485,7 @@ func (p *Resolver) insertEntry(entry, prev *model.ProcessCacheEntry, source uint p.cacheSize.Inc() } -func (p *Resolver) insertForkEntry(entry *model.ProcessCacheEntry, inode uint64, source uint64) { +func (p *EBPFResolver) insertForkEntry(entry *model.ProcessCacheEntry, inode uint64, source uint64) { prev := p.entryCache[entry.Pid] if prev != nil { // this shouldn't happen but it is better to exit the prev and let the new one replace it @@ -519,7 +513,7 @@ func (p *Resolver) insertForkEntry(entry *model.ProcessCacheEntry, inode uint64, p.insertEntry(entry, prev, source) } -func (p *Resolver) insertExecEntry(entry *model.ProcessCacheEntry, inode uint64, source uint64) { +func (p *EBPFResolver) insertExecEntry(entry *model.ProcessCacheEntry, inode uint64, source uint64) { prev := p.entryCache[entry.Pid] if prev != nil { if inode != 0 && prev.FileEvent.Inode != inode { @@ -541,7 +535,7 @@ func (p *Resolver) insertExecEntry(entry *model.ProcessCacheEntry, inode uint64, p.insertEntry(entry, prev, source) } -func (p *Resolver) deleteEntry(pid uint32, exitTime time.Time) { +func (p *EBPFResolver) deleteEntry(pid uint32, exitTime time.Time) { // Start by updating the exit timestamp of the pid cache entry entry, ok := p.entryCache[pid] if !ok { @@ -558,7 +552,7 @@ func (p *Resolver) deleteEntry(pid uint32, exitTime time.Time) { } // DeleteEntry tries to delete an entry in the process cache -func (p *Resolver) DeleteEntry(pid uint32, exitTime time.Time) { +func (p *EBPFResolver) DeleteEntry(pid uint32, exitTime time.Time) { p.Lock() defer p.Unlock() @@ -566,14 +560,14 @@ func (p *Resolver) DeleteEntry(pid uint32, exitTime time.Time) { } // Resolve returns the cache entry for the given pid -func (p *Resolver) Resolve(pid, tid uint32, inode uint64, useProcFS bool) *model.ProcessCacheEntry { +func (p *EBPFResolver) Resolve(pid, tid uint32, inode uint64, useProcFS bool) *model.ProcessCacheEntry { p.Lock() defer p.Unlock() return p.resolve(pid, tid, inode, useProcFS) } -func (p *Resolver) resolve(pid, tid uint32, inode uint64, useProcFS bool) *model.ProcessCacheEntry { +func (p *EBPFResolver) resolve(pid, tid uint32, inode uint64, useProcFS bool) *model.ProcessCacheEntry { if entry := p.resolveFromCache(pid, tid, inode); entry != nil { p.hitsStats[metrics.CacheTag].Inc() return entry @@ -615,7 +609,7 @@ func setPathname(fileEvent *model.FileEvent, pathnameStr string) { fileEvent.SetBasenameStr(path.Base(pathnameStr)) } -func (p *Resolver) resolveFileFieldsPath(e *model.FileFields, pce *model.ProcessCacheEntry, ctrCtx *model.ContainerContext) (string, error) { +func (p *EBPFResolver) resolveFileFieldsPath(e *model.FileFields, pce *model.ProcessCacheEntry, ctrCtx *model.ContainerContext) (string, error) { var ( pathnameStr string err error @@ -640,7 +634,7 @@ func (p *Resolver) resolveFileFieldsPath(e *model.FileFields, pce *model.Process } // SetProcessPath resolves process file path -func (p *Resolver) SetProcessPath(fileEvent *model.FileEvent, pce *model.ProcessCacheEntry, ctrCtx *model.ContainerContext) (string, error) { +func (p *EBPFResolver) SetProcessPath(fileEvent *model.FileEvent, pce *model.ProcessCacheEntry, ctrCtx *model.ContainerContext) (string, error) { onError := func(pathnameStr string, err error) (string, error) { fileEvent.SetPathnameStr("") fileEvent.SetBasenameStr("") @@ -669,7 +663,7 @@ func IsBusybox(pathname string) bool { } // SetProcessSymlink resolves process file symlink path -func (p *Resolver) SetProcessSymlink(entry *model.ProcessCacheEntry) { +func (p *EBPFResolver) SetProcessSymlink(entry *model.ProcessCacheEntry) { // TODO: busybox workaround only for now if IsBusybox(entry.FileEvent.PathnameStr) { arg0, _ := GetProcessArgv0(&entry.Process) @@ -683,7 +677,7 @@ func (p *Resolver) SetProcessSymlink(entry *model.ProcessCacheEntry) { } // SetProcessFilesystem resolves process file system -func (p *Resolver) SetProcessFilesystem(entry *model.ProcessCacheEntry) (string, error) { +func (p *EBPFResolver) SetProcessFilesystem(entry *model.ProcessCacheEntry) (string, error) { if entry.FileEvent.MountID != 0 { fs, err := p.mountResolver.ResolveFilesystem(entry.FileEvent.MountID, entry.FileEvent.Device, entry.Pid, entry.ContainerID) if err != nil { @@ -696,20 +690,20 @@ func (p *Resolver) SetProcessFilesystem(entry *model.ProcessCacheEntry) (string, } // ApplyBootTime realign timestamp from the boot time -func (p *Resolver) ApplyBootTime(entry *model.ProcessCacheEntry) { +func (p *EBPFResolver) ApplyBootTime(entry *model.ProcessCacheEntry) { entry.ExecTime = p.timeResolver.ApplyBootTime(entry.ExecTime) entry.ForkTime = p.timeResolver.ApplyBootTime(entry.ForkTime) entry.ExitTime = p.timeResolver.ApplyBootTime(entry.ExitTime) } // ResolveFromCache resolves cache entry from the cache -func (p *Resolver) ResolveFromCache(pid, tid uint32, inode uint64) *model.ProcessCacheEntry { +func (p *EBPFResolver) ResolveFromCache(pid, tid uint32, inode uint64) *model.ProcessCacheEntry { p.Lock() defer p.Unlock() return p.resolveFromCache(pid, tid, inode) } -func (p *Resolver) resolveFromCache(pid, tid uint32, inode uint64) *model.ProcessCacheEntry { +func (p *EBPFResolver) resolveFromCache(pid, tid uint32, inode uint64) *model.ProcessCacheEntry { entry, exists := p.entryCache[pid] if !exists { return nil @@ -729,7 +723,7 @@ func (p *Resolver) resolveFromCache(pid, tid uint32, inode uint64) *model.Proces } // ResolveNewProcessCacheEntry resolves the context fields of a new process cache entry parsed from kernel data -func (p *Resolver) ResolveNewProcessCacheEntry(entry *model.ProcessCacheEntry, ctrCtx *model.ContainerContext) error { +func (p *EBPFResolver) ResolveNewProcessCacheEntry(entry *model.ProcessCacheEntry, ctrCtx *model.ContainerContext) error { if _, err := p.SetProcessPath(&entry.FileEvent, entry, ctrCtx); err != nil { return &spath.ErrPathResolution{Err: fmt.Errorf("failed to resolve exec path: %w", err)} } @@ -757,13 +751,13 @@ func (p *Resolver) ResolveNewProcessCacheEntry(entry *model.ProcessCacheEntry, c } // ResolveFromKernelMaps resolves the entry from the kernel maps -func (p *Resolver) ResolveFromKernelMaps(pid, tid uint32, inode uint64) *model.ProcessCacheEntry { +func (p *EBPFResolver) ResolveFromKernelMaps(pid, tid uint32, inode uint64) *model.ProcessCacheEntry { p.Lock() defer p.Unlock() return p.resolveFromKernelMaps(pid, tid, inode) } -func (p *Resolver) resolveFromKernelMaps(pid, tid uint32, inode uint64) *model.ProcessCacheEntry { +func (p *EBPFResolver) resolveFromKernelMaps(pid, tid uint32, inode uint64) *model.ProcessCacheEntry { pidb := make([]byte, 4) model.ByteOrder.PutUint32(pidb, pid) @@ -839,13 +833,13 @@ func IsKThread(ppid, pid uint32) bool { } // ResolveFromProcfs resolves the entry from procfs -func (p *Resolver) ResolveFromProcfs(pid uint32) *model.ProcessCacheEntry { +func (p *EBPFResolver) ResolveFromProcfs(pid uint32) *model.ProcessCacheEntry { p.Lock() defer p.Unlock() return p.resolveFromProcfs(pid, procResolveMaxDepth) } -func (p *Resolver) resolveFromProcfs(pid uint32, maxDepth int) *model.ProcessCacheEntry { +func (p *EBPFResolver) resolveFromProcfs(pid uint32, maxDepth int) *model.ProcessCacheEntry { if maxDepth < 1 { seclog.Tracef("max depth reached during procfs resolution: %d", pid) return nil @@ -895,7 +889,7 @@ func (p *Resolver) resolveFromProcfs(pid uint32, maxDepth int) *model.ProcessCac } // SetProcessArgs set arguments to cache entry -func (p *Resolver) SetProcessArgs(pce *model.ProcessCacheEntry) { +func (p *EBPFResolver) SetProcessArgs(pce *model.ProcessCacheEntry) { if entry, found := p.argsEnvsCache.Get(pce.ArgsID); found { if pce.ArgsTruncated { p.argsTruncated.Inc() @@ -943,7 +937,7 @@ func GetProcessArgv0(pr *model.Process) (string, bool) { } // GetProcessArgvScrubbed returns the scrubbed args of the event as an array -func (p *Resolver) GetProcessArgvScrubbed(pr *model.Process) ([]string, bool) { +func (p *EBPFResolver) GetProcessArgvScrubbed(pr *model.Process) ([]string, bool) { if pr.ArgsEntry == nil || pr.ScrubbedArgvResolved { return pr.Argv, pr.ArgsTruncated } @@ -955,13 +949,15 @@ func (p *Resolver) GetProcessArgvScrubbed(pr *model.Process) ([]string, bool) { argv, _ = p.scrubber.ScrubCommand(argv) pr.ArgsEntry.Values = []string{pr.ArgsEntry.Values[0]} pr.ArgsEntry.Values = append(pr.ArgsEntry.Values, argv...) + + pr.ScrubbedArgvResolved = true } return argv, truncated } // SetProcessEnvs set envs to cache entry -func (p *Resolver) SetProcessEnvs(pce *model.ProcessCacheEntry) { +func (p *EBPFResolver) SetProcessEnvs(pce *model.ProcessCacheEntry) { if entry, found := p.argsEnvsCache.Get(pce.EnvsID); found { if pce.EnvsTruncated { p.envsTruncated.Inc() @@ -980,7 +976,7 @@ func (p *Resolver) SetProcessEnvs(pce *model.ProcessCacheEntry) { } // GetProcessEnvs returns the envs of the event -func (p *Resolver) GetProcessEnvs(pr *model.Process) ([]string, bool) { +func (p *EBPFResolver) GetProcessEnvs(pr *model.Process) ([]string, bool) { if pr.EnvsEntry == nil { return pr.Envs, pr.EnvsTruncated } @@ -992,7 +988,7 @@ func (p *Resolver) GetProcessEnvs(pr *model.Process) ([]string, bool) { } // GetProcessEnvp returns the unscrubbed envs of the event with their values. Use with caution. -func (p *Resolver) GetProcessEnvp(pr *model.Process) ([]string, bool) { +func (p *EBPFResolver) GetProcessEnvp(pr *model.Process) ([]string, bool) { if pr.EnvsEntry == nil { return pr.Envp, pr.EnvsTruncated } @@ -1003,7 +999,7 @@ func (p *Resolver) GetProcessEnvp(pr *model.Process) ([]string, bool) { } // SetProcessTTY resolves TTY and cache the result -func (p *Resolver) SetProcessTTY(pce *model.ProcessCacheEntry) string { +func (p *EBPFResolver) SetProcessTTY(pce *model.ProcessCacheEntry) string { if pce.TTYName == "" && p.opts.ttyFallbackEnabled { tty := utils.PidTTY(pce.Pid) pce.TTYName = tty @@ -1012,7 +1008,7 @@ func (p *Resolver) SetProcessTTY(pce *model.ProcessCacheEntry) string { } // SetProcessUsersGroups resolves and set users and groups -func (p *Resolver) SetProcessUsersGroups(pce *model.ProcessCacheEntry) { +func (p *EBPFResolver) SetProcessUsersGroups(pce *model.ProcessCacheEntry) { pce.User, _ = p.userGroupResolver.ResolveUser(int(pce.Credentials.UID), pce.ContainerID) pce.EUser, _ = p.userGroupResolver.ResolveUser(int(pce.Credentials.EUID), pce.ContainerID) pce.FSUser, _ = p.userGroupResolver.ResolveUser(int(pce.Credentials.FSUID), pce.ContainerID) @@ -1023,14 +1019,14 @@ func (p *Resolver) SetProcessUsersGroups(pce *model.ProcessCacheEntry) { } // Get returns the cache entry for a specified pid -func (p *Resolver) Get(pid uint32) *model.ProcessCacheEntry { +func (p *EBPFResolver) Get(pid uint32) *model.ProcessCacheEntry { p.RLock() defer p.RUnlock() return p.entryCache[pid] } // UpdateUID updates the credentials of the provided pid -func (p *Resolver) UpdateUID(pid uint32, e *model.Event) { +func (p *EBPFResolver) UpdateUID(pid uint32, e *model.Event) { if e.ProcessContext.Pid != e.ProcessContext.Tid { return } @@ -1049,7 +1045,7 @@ func (p *Resolver) UpdateUID(pid uint32, e *model.Event) { } // UpdateGID updates the credentials of the provided pid -func (p *Resolver) UpdateGID(pid uint32, e *model.Event) { +func (p *EBPFResolver) UpdateGID(pid uint32, e *model.Event) { if e.ProcessContext.Pid != e.ProcessContext.Tid { return } @@ -1068,7 +1064,7 @@ func (p *Resolver) UpdateGID(pid uint32, e *model.Event) { } // UpdateCapset updates the credentials of the provided pid -func (p *Resolver) UpdateCapset(pid uint32, e *model.Event) { +func (p *EBPFResolver) UpdateCapset(pid uint32, e *model.Event) { if e.ProcessContext.Pid != e.ProcessContext.Tid { return } @@ -1083,7 +1079,7 @@ func (p *Resolver) UpdateCapset(pid uint32, e *model.Event) { } // Start starts the resolver -func (p *Resolver) Start(ctx context.Context) error { +func (p *EBPFResolver) Start(ctx context.Context) error { var err error if p.execFileCacheMap, err = managerhelper.Map(p.manager, "exec_file_cache"); err != nil { return err @@ -1102,7 +1098,7 @@ func (p *Resolver) Start(ctx context.Context) error { return nil } -func (p *Resolver) cacheFlush(ctx context.Context) { +func (p *EBPFResolver) cacheFlush(ctx context.Context) { ticker := time.NewTicker(2 * time.Minute) defer ticker.Stop() @@ -1134,7 +1130,7 @@ func (p *Resolver) cacheFlush(ctx context.Context) { } // SyncCache snapshots /proc for the provided pid. This method returns true if it updated the process cache. -func (p *Resolver) SyncCache(proc *process.Process) bool { +func (p *EBPFResolver) SyncCache(proc *process.Process) bool { // Only a R lock is necessary to check if the entry exists, but if it exists, we'll update it, so a RW lock is // required. p.Lock() @@ -1150,7 +1146,7 @@ func (p *Resolver) SyncCache(proc *process.Process) bool { return ret } -func (p *Resolver) setAncestor(pce *model.ProcessCacheEntry) { +func (p *EBPFResolver) setAncestor(pce *model.ProcessCacheEntry) { parent := p.entryCache[pce.PPid] if parent != nil { pce.SetAncestor(parent) @@ -1158,7 +1154,7 @@ func (p *Resolver) setAncestor(pce *model.ProcessCacheEntry) { } // syncCache snapshots /proc for the provided pid. This method returns true if it updated the process cache. -func (p *Resolver) syncCache(proc *process.Process, filledProc *utils.FilledProcess, source uint64) (*model.ProcessCacheEntry, bool) { +func (p *EBPFResolver) syncCache(proc *process.Process, filledProc *utils.FilledProcess, source uint64) (*model.ProcessCacheEntry, bool) { pid := uint32(proc.Pid) // Check if an entry is already in cache for the given pid. @@ -1182,7 +1178,7 @@ func (p *Resolver) syncCache(proc *process.Process, filledProc *utils.FilledProc p.setAncestor(entry) - p.insertEntry(entry, p.entryCache[pid], model.ProcessCacheEntryFromSnapshot) + p.insertEntry(entry, p.entryCache[pid], source) bootTime := p.timeResolver.GetBootTime() @@ -1211,7 +1207,7 @@ func (p *Resolver) syncCache(proc *process.Process, filledProc *utils.FilledProc return entry, true } -func (p *Resolver) dumpEntry(writer io.Writer, entry *model.ProcessCacheEntry, already map[string]bool, withArgs bool) { +func (p *EBPFResolver) dumpEntry(writer io.Writer, entry *model.ProcessCacheEntry, already map[string]bool, withArgs bool) { for entry != nil { label := fmt.Sprintf("%s:%d", entry.Comm, entry.Pid) if _, exists := already[label]; !exists { @@ -1244,7 +1240,7 @@ func (p *Resolver) dumpEntry(writer io.Writer, entry *model.ProcessCacheEntry, a } // Dump create a temp file and dump the cache -func (p *Resolver) Dump(withArgs bool) (string, error) { +func (p *EBPFResolver) Dump(withArgs bool) (string, error) { dump, err := os.CreateTemp("/tmp", "process-cache-dump-") if err != nil { return "", err @@ -1275,24 +1271,24 @@ func (p *Resolver) Dump(withArgs bool) (string, error) { } // getCacheSize returns the cache size of the process resolver -func (p *Resolver) getCacheSize() float64 { +func (p *EBPFResolver) getCacheSize() float64 { p.RLock() defer p.RUnlock() return float64(len(p.entryCache)) } // getEntryCacheSize returns the cache size of the process resolver -func (p *Resolver) getEntryCacheSize() float64 { +func (p *EBPFResolver) getEntryCacheSize() float64 { return float64(p.cacheSize.Load()) } // SetState sets the process resolver state -func (p *Resolver) SetState(state int64) { +func (p *EBPFResolver) SetState(state int64) { p.state.Store(state) } // Walk iterates through the entire tree and call the provided callback on each entry -func (p *Resolver) Walk(callback func(entry *model.ProcessCacheEntry)) { +func (p *EBPFResolver) Walk(callback func(entry *model.ProcessCacheEntry)) { p.RLock() defer p.RUnlock() @@ -1301,17 +1297,17 @@ func (p *Resolver) Walk(callback func(entry *model.ProcessCacheEntry)) { } } -// NewResolver returns a new process resolver -func NewResolver(manager *manager.Manager, config *config.Config, statsdClient statsd.ClientInterface, +// NewEBPFResolver returns a new process resolver +func NewEBPFResolver(manager *manager.Manager, config *config.Config, statsdClient statsd.ClientInterface, scrubber *procutil.DataScrubber, containerResolver *container.Resolver, mountResolver *mount.Resolver, cgroupResolver *cgroup.Resolver, userGroupResolver *usergroup.Resolver, timeResolver *stime.Resolver, - pathResolver spath.ResolverInterface, opts *ResolverOpts) (*Resolver, error) { + pathResolver spath.ResolverInterface, opts *ResolverOpts) (*EBPFResolver, error) { argsEnvsCache, err := simplelru.NewLRU[uint32, *argsEnvsCacheEntry](maxParallelArgsEnvs, nil) if err != nil { return nil, err } - p := &Resolver{ + p := &EBPFResolver{ manager: manager, config: config, statsdClient: statsdClient, @@ -1345,7 +1341,7 @@ func NewResolver(manager *manager.Manager, config *config.Config, statsdClient s for _, t := range metrics.AllTypesTags { p.hitsStats[t] = atomic.NewInt64(0) } - p.processCacheEntryPool = NewProcessCacheEntryPool(p) + p.processCacheEntryPool = NewProcessCacheEntryPool(func() { p.cacheSize.Dec() }) // Create rate limiter that allows for 128 pids limiter, err := utils.NewLimiter[uint32](128, numAllowedPIDsToResolvePerPeriod, procFallbackLimiterPeriod) @@ -1356,24 +1352,3 @@ func NewResolver(manager *manager.Manager, config *config.Config, statsdClient s return p, nil } - -// WithEnvsValue specifies envs with value -func (o *ResolverOpts) WithEnvsValue(envsWithValue []string) *ResolverOpts { - for _, envVar := range envsWithValue { - o.envsWithValue[envVar] = true - } - return o -} - -// WithTTYFallbackEnabled enables the TTY fallback -func (o *ResolverOpts) WithTTYFallbackEnabled() *ResolverOpts { - o.ttyFallbackEnabled = true - return o -} - -// NewResolverOpts returns a new set of process resolver options -func NewResolverOpts() *ResolverOpts { - return &ResolverOpts{ - envsWithValue: make(map[string]bool), - } -} diff --git a/pkg/security/resolvers/process/resolver_ebpfless.go b/pkg/security/resolvers/process/resolver_ebpfless.go new file mode 100644 index 0000000000000..8083f46033eaf --- /dev/null +++ b/pkg/security/resolvers/process/resolver_ebpfless.go @@ -0,0 +1,234 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux + +// Package process holds process related files +package process + +import ( + "context" + "errors" + "fmt" + "path/filepath" + "sync" + "time" + + "go.uber.org/atomic" + + "github.com/DataDog/datadog-go/v5/statsd" + + "github.com/DataDog/datadog-agent/pkg/process/procutil" + "github.com/DataDog/datadog-agent/pkg/security/metrics" + "github.com/DataDog/datadog-agent/pkg/security/probe/config" + "github.com/DataDog/datadog-agent/pkg/security/secl/model" +) + +// EBPFLessResolver defines a resolver +type EBPFLessResolver struct { + sync.RWMutex + entryCache map[uint32]*model.ProcessCacheEntry + opts ResolverOpts + scrubber *procutil.DataScrubber + statsdClient statsd.ClientInterface + + // stats + cacheSize *atomic.Int64 + + processCacheEntryPool *Pool +} + +// NewEBPFLessResolver returns a new process resolver +func NewEBPFLessResolver(_ *config.Config, statsdClient statsd.ClientInterface, scrubber *procutil.DataScrubber, opts *ResolverOpts) (*EBPFLessResolver, error) { + p := &EBPFLessResolver{ + entryCache: make(map[uint32]*model.ProcessCacheEntry), + opts: *opts, + scrubber: scrubber, + cacheSize: atomic.NewInt64(0), + statsdClient: statsdClient, + } + + p.processCacheEntryPool = NewProcessCacheEntryPool(func() { p.cacheSize.Dec() }) + + return p, nil +} + +func (p *EBPFLessResolver) deleteEntry(pid uint32, exitTime time.Time) { + entry, ok := p.entryCache[pid] + if !ok { + return + } + + entry.Exit(exitTime) + delete(p.entryCache, entry.Pid) + entry.Release() +} + +// DeleteEntry tries to delete an entry in the process cache +func (p *EBPFLessResolver) DeleteEntry(pid uint32, exitTime time.Time) { + p.Lock() + defer p.Unlock() + + p.deleteEntry(pid, exitTime) +} + +// AddForkEntry adds an entry to the local cache and returns the newly created entry +func (p *EBPFLessResolver) AddForkEntry(pid uint32, ppid uint32) *model.ProcessCacheEntry { + entry := p.processCacheEntryPool.Get() + entry.PIDContext.Pid = pid + entry.PPid = ppid + + p.Lock() + defer p.Unlock() + + p.insertForkEntry(entry) + + return entry +} + +// AddExecEntry adds an entry to the local cache and returns the newly created entry +func (p *EBPFLessResolver) AddExecEntry(pid uint32, file string, argv []string, envs []string, ctrID string) *model.ProcessCacheEntry { + entry := p.processCacheEntryPool.Get() + entry.PIDContext.Pid = pid + + entry.Process.ArgsEntry = &model.ArgsEntry{Values: argv} + if len(argv) > 0 { + entry.Process.Comm = argv[0] + entry.Process.Argv0 = argv[0] + } + + entry.Process.EnvsEntry = &model.EnvsEntry{Values: envs} + + entry.Process.FileEvent.PathnameStr = file + entry.Process.FileEvent.BasenameStr = filepath.Base(entry.Process.FileEvent.PathnameStr) + entry.Process.ContainerID = ctrID + + // TODO fix timestamp + entry.ExecTime = time.Now() + + p.Lock() + defer p.Unlock() + + p.insertExecEntry(entry) + + return entry +} + +func (p *EBPFLessResolver) insertEntry(entry, prev *model.ProcessCacheEntry) { + p.entryCache[entry.Pid] = entry + entry.Retain() + + if prev != nil { + prev.Release() + } + + p.cacheSize.Inc() +} + +func (p *EBPFLessResolver) insertForkEntry(entry *model.ProcessCacheEntry) { + prev := p.entryCache[entry.Pid] + if prev != nil { + // this shouldn't happen but it is better to exit the prev and let the new one replace it + prev.Exit(entry.ForkTime) + } + + if entry.Pid != 1 { + parent := p.entryCache[entry.PPid] + if parent != nil { + parent.Fork(entry) + } + } + + p.insertEntry(entry, prev) +} + +func (p *EBPFLessResolver) insertExecEntry(entry *model.ProcessCacheEntry) { + prev := p.entryCache[entry.Pid] + if prev != nil { + prev.Exec(entry) + } + + p.insertEntry(entry, prev) +} + +// Resolve returns the cache entry for the given pid +func (p *EBPFLessResolver) Resolve(pid uint32) *model.ProcessCacheEntry { + p.Lock() + defer p.Unlock() + if e, ok := p.entryCache[pid]; ok { + return e + } + return nil +} + +// getCacheSize returns the cache size of the process resolver +func (p *EBPFLessResolver) getCacheSize() float64 { + p.RLock() + defer p.RUnlock() + return float64(len(p.entryCache)) +} + +// SendStats sends process resolver metrics +func (p *EBPFLessResolver) SendStats() error { + if err := p.statsdClient.Gauge(metrics.MetricProcessResolverCacheSize, p.getCacheSize(), []string{}, 1.0); err != nil { + return fmt.Errorf("failed to send process_resolver cache_size metric: %w", err) + } + + return nil +} + +// Start starts the resolver +func (p *EBPFLessResolver) Start(_ context.Context) error { + return nil +} + +// Snapshot snapshot existing entryCache +func (p *EBPFLessResolver) Snapshot() {} + +// Dump create a temp file and dump the cache +func (p *EBPFLessResolver) Dump(_ bool) (string, error) { + return "", errors.New("not supported") +} + +// GetProcessArgvScrubbed returns the scrubbed args of the event as an array +func (p *EBPFLessResolver) GetProcessArgvScrubbed(pr *model.Process) ([]string, bool) { + if pr.ArgsEntry == nil || pr.ScrubbedArgvResolved { + return pr.Argv, pr.ArgsTruncated + } + + argv, truncated := GetProcessArgv(pr) + + if p.scrubber != nil && len(argv) > 0 { + // replace with the scrubbed version + argv, _ = p.scrubber.ScrubCommand(argv) + pr.ArgsEntry.Values = []string{pr.ArgsEntry.Values[0]} + pr.ArgsEntry.Values = append(pr.ArgsEntry.Values, argv...) + } + + return argv, truncated +} + +// GetProcessEnvs returns the envs of the event +func (p *EBPFLessResolver) GetProcessEnvs(pr *model.Process) ([]string, bool) { + if pr.EnvsEntry == nil { + return pr.Envs, pr.EnvsTruncated + } + + keys, truncated := pr.EnvsEntry.FilterEnvs(p.opts.envsWithValue) + pr.Envs = keys + pr.EnvsTruncated = pr.EnvsTruncated || truncated + return pr.Envs, pr.EnvsTruncated +} + +// GetProcessEnvp returns the unscrubbed envs of the event with their values. Use with caution. +func (p *EBPFLessResolver) GetProcessEnvp(pr *model.Process) ([]string, bool) { + if pr.EnvsEntry == nil { + return pr.Envp, pr.EnvsTruncated + } + + pr.Envp = pr.EnvsEntry.Values + pr.EnvsTruncated = pr.EnvsTruncated || pr.EnvsEntry.Truncated + return pr.Envp, pr.EnvsTruncated +} diff --git a/pkg/security/resolvers/process/resolver_test.go b/pkg/security/resolvers/process/resolver_test.go index 7eb3d61782a70..523cdc30555e8 100644 --- a/pkg/security/resolvers/process/resolver_test.go +++ b/pkg/security/resolvers/process/resolver_test.go @@ -20,7 +20,7 @@ import ( "github.com/DataDog/datadog-go/v5/statsd" ) -func testCacheSize(t *testing.T, resolver *Resolver) { +func testCacheSize(t *testing.T, resolver *EBPFResolver) { err := retry.Do( func() error { if resolver.cacheSize.Load() == 0 { @@ -34,7 +34,7 @@ func testCacheSize(t *testing.T, resolver *Resolver) { } func TestFork1st(t *testing.T) { - resolver, err := NewResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) + resolver, err := NewEBPFResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) if err != nil { t.Fatal(err) } @@ -73,7 +73,7 @@ func TestFork1st(t *testing.T) { } func TestFork2nd(t *testing.T) { - resolver, err := NewResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) + resolver, err := NewEBPFResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) if err != nil { t.Fatal(err) } @@ -114,7 +114,7 @@ func TestFork2nd(t *testing.T) { } func TestForkExec(t *testing.T) { - resolver, err := NewResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) + resolver, err := NewEBPFResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) if err != nil { t.Fatal(err) } @@ -170,7 +170,7 @@ func TestForkExec(t *testing.T) { } func TestOrphanExec(t *testing.T) { - resolver, err := NewResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) + resolver, err := NewEBPFResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) if err != nil { t.Fatal(err) } @@ -226,7 +226,7 @@ func TestOrphanExec(t *testing.T) { } func TestForkExecExec(t *testing.T) { - resolver, err := NewResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) + resolver, err := NewEBPFResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) if err != nil { t.Fatal(err) } @@ -297,7 +297,7 @@ func TestForkExecExec(t *testing.T) { } func TestForkReuse(t *testing.T) { - resolver, err := NewResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) + resolver, err := NewEBPFResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) if err != nil { t.Fatal(err) } @@ -392,7 +392,7 @@ func TestForkReuse(t *testing.T) { } func TestForkForkExec(t *testing.T) { - resolver, err := NewResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) + resolver, err := NewEBPFResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) if err != nil { t.Fatal(err) } @@ -468,7 +468,7 @@ func TestForkForkExec(t *testing.T) { } func TestExecBomb(t *testing.T) { - resolver, err := NewResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) + resolver, err := NewEBPFResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) if err != nil { t.Fatal(err) } @@ -537,7 +537,7 @@ func TestExecBomb(t *testing.T) { } func TestExecLostFork(t *testing.T) { - resolver, err := NewResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) + resolver, err := NewEBPFResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) if err != nil { t.Fatal(err) } @@ -579,7 +579,7 @@ func TestExecLostFork(t *testing.T) { } func TestExecLostExec(t *testing.T) { - resolver, err := NewResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) + resolver, err := NewEBPFResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) if err != nil { t.Fatal(err) } @@ -622,7 +622,7 @@ func TestExecLostExec(t *testing.T) { } func TestIsExecChildRuntime(t *testing.T) { - resolver, err := NewResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) + resolver, err := NewEBPFResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) if err != nil { t.Fatal(err) } @@ -683,7 +683,7 @@ func TestIsExecChildRuntime(t *testing.T) { } func TestIsExecChildSnapshot(t *testing.T) { - resolver, err := NewResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) + resolver, err := NewEBPFResolver(nil, nil, &statsd.NoOpClient{}, nil, nil, nil, nil, nil, nil, nil, NewResolverOpts()) if err != nil { t.Fatal(err) } diff --git a/pkg/security/resolvers/process/resolver_windows.go b/pkg/security/resolvers/process/resolver_windows.go index 68e01614f19ee..bdfcfb00ed6ea 100644 --- a/pkg/security/resolvers/process/resolver_windows.go +++ b/pkg/security/resolvers/process/resolver_windows.go @@ -3,8 +3,6 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build windows - // Package process holds process related files package process @@ -44,11 +42,6 @@ type Resolver struct { processCacheEntryPool *Pool } -// ResolverOpts options of resolver -type ResolverOpts struct { - envsWithValue map[string]bool -} - // NewResolver returns a new process resolver func NewResolver(config *config.Config, statsdClient statsd.ClientInterface, scrubber *procutil.DataScrubber, //nolint:revive // TODO fix revive unused-parameter opts ResolverOpts) (*Resolver, error) { @@ -61,16 +54,11 @@ func NewResolver(config *config.Config, statsdClient statsd.ClientInterface, scr statsdClient: statsdClient, } - p.processCacheEntryPool = NewProcessCacheEntryPool(p) + p.processCacheEntryPool = NewProcessCacheEntryPool(func() { p.cacheSize.Dec() }) return p, nil } -// NewResolverOpts returns a new set of process resolver options -func NewResolverOpts() ResolverOpts { - return ResolverOpts{} -} - func (p *Resolver) insertEntry(entry *model.ProcessCacheEntry) { // PID collision if prev := p.processes[entry.Pid]; prev != nil { diff --git a/pkg/security/resolvers/resolvers_linux.go b/pkg/security/resolvers/resolvers_ebpf.go similarity index 90% rename from pkg/security/resolvers/resolvers_linux.go rename to pkg/security/resolvers/resolvers_ebpf.go index b81a3c9383c8d..0167a28cae9c4 100644 --- a/pkg/security/resolvers/resolvers_linux.go +++ b/pkg/security/resolvers/resolvers_ebpf.go @@ -40,16 +40,8 @@ import ( "github.com/DataDog/datadog-agent/pkg/util/log" ) -// Opts defines common options -type Opts struct { - PathResolutionEnabled bool - TagsResolver tags.Resolver - UseRingBuffer bool - TTYFallbackEnabled bool -} - -// Resolvers holds the list of the event attribute resolvers -type Resolvers struct { +// EBPFResolvers holds the list of the event attribute resolvers +type EBPFResolvers struct { manager *manager.Manager MountResolver *mount.Resolver ContainerResolver *container.Resolver @@ -57,7 +49,7 @@ type Resolvers struct { UserGroupResolver *usergroup.Resolver TagsResolver tags.Resolver DentryResolver *dentry.Resolver - ProcessResolver *process.Resolver + ProcessResolver *process.EBPFResolver NamespaceResolver *netns.Resolver CGroupResolver *cgroup.Resolver TCResolver *tc.Resolver @@ -67,8 +59,8 @@ type Resolvers struct { UserSessions *usersessions.Resolver } -// NewResolvers creates a new instance of Resolvers -func NewResolvers(config *config.Config, manager *manager.Manager, statsdClient statsd.ClientInterface, scrubber *procutil.DataScrubber, eRPC *erpc.ERPC, opts Opts) (*Resolvers, error) { +// NewEBPFResolvers creates a new instance of EBPFResolvers +func NewEBPFResolvers(config *config.Config, manager *manager.Manager, statsdClient statsd.ClientInterface, scrubber *procutil.DataScrubber, eRPC *erpc.ERPC, opts Opts) (*EBPFResolvers, error) { dentryResolver, err := dentry.NewResolver(config.Probe, statsdClient, eRPC) if err != nil { return nil, err @@ -145,7 +137,7 @@ func NewResolvers(config *config.Config, manager *manager.Manager, statsdClient processOpts.WithTTYFallbackEnabled() } - processResolver, err := process.NewResolver(manager, config.Probe, statsdClient, + processResolver, err := process.NewEBPFResolver(manager, config.Probe, statsdClient, scrubber, containerResolver, mountResolver, cgroupsResolver, userGroupResolver, timeResolver, pathResolver, processOpts) if err != nil { return nil, err @@ -160,7 +152,7 @@ func NewResolvers(config *config.Config, manager *manager.Manager, statsdClient return nil, err } - resolvers := &Resolvers{ + resolvers := &EBPFResolvers{ manager: manager, MountResolver: mountResolver, ContainerResolver: containerResolver, @@ -182,7 +174,7 @@ func NewResolvers(config *config.Config, manager *manager.Manager, statsdClient } // Start the resolvers -func (r *Resolvers) Start(ctx context.Context) error { +func (r *EBPFResolvers) Start(ctx context.Context) error { if err := r.ProcessResolver.Start(ctx); err != nil { return err } @@ -207,7 +199,7 @@ func (r *Resolvers) Start(ctx context.Context) error { } // Snapshot collects data on the current state of the system to populate user space and kernel space caches. -func (r *Resolvers) Snapshot() error { +func (r *EBPFResolvers) Snapshot() error { if err := r.snapshot(); err != nil { return fmt.Errorf("unable to snapshot processes: %w", err) } @@ -228,7 +220,7 @@ func (r *Resolvers) Snapshot() error { } // snapshot internal version of Snapshot. Calls the relevant resolvers to sync their caches. -func (r *Resolvers) snapshot() error { +func (r *EBPFResolvers) snapshot() error { // List all processes, to trigger the process and mount snapshots processes, err := utils.GetProcesses() if err != nil { @@ -288,7 +280,7 @@ func (r *Resolvers) snapshot() error { } // Close cleans up any underlying resolver that requires a cleanup -func (r *Resolvers) Close() error { +func (r *EBPFResolvers) Close() error { // clean up the dentry resolver eRPC segment return r.DentryResolver.Close() } diff --git a/pkg/security/resolvers/resolvers_ebpfless.go b/pkg/security/resolvers/resolvers_ebpfless.go new file mode 100644 index 0000000000000..1297ca5fe894d --- /dev/null +++ b/pkg/security/resolvers/resolvers_ebpfless.go @@ -0,0 +1,76 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux + +// Package resolvers holds resolvers related files +package resolvers + +import ( + "context" + + "github.com/DataDog/datadog-go/v5/statsd" + + "github.com/DataDog/datadog-agent/pkg/process/procutil" + "github.com/DataDog/datadog-agent/pkg/security/config" + "github.com/DataDog/datadog-agent/pkg/security/resolvers/container" + "github.com/DataDog/datadog-agent/pkg/security/resolvers/process" + "github.com/DataDog/datadog-agent/pkg/security/resolvers/tags" +) + +// EBPFLessResolvers holds the list of the event attribute resolvers +type EBPFLessResolvers struct { + ContainerResolver *container.Resolver + TagsResolver tags.Resolver + ProcessResolver *process.EBPFLessResolver +} + +// NewEBPFLessResolvers creates a new instance of EBPFLessResolvers +func NewEBPFLessResolvers(config *config.Config, statsdClient statsd.ClientInterface, scrubber *procutil.DataScrubber, opts Opts) (*EBPFLessResolvers, error) { + var tagsResolver tags.Resolver + if opts.TagsResolver != nil { + tagsResolver = opts.TagsResolver + } else { + tagsResolver = tags.NewResolver(config.Probe) + } + + processOpts := process.NewResolverOpts() + processOpts.WithEnvsValue(config.Probe.EnvsWithValue) + + processResolver, err := process.NewEBPFLessResolver(config.Probe, statsdClient, scrubber, processOpts) + if err != nil { + return nil, err + } + + resolvers := &EBPFLessResolvers{ + TagsResolver: tagsResolver, + ProcessResolver: processResolver, + } + + return resolvers, nil +} + +// Start the resolvers +func (r *EBPFLessResolvers) Start(ctx context.Context) error { + if err := r.ProcessResolver.Start(ctx); err != nil { + return err + } + + if err := r.TagsResolver.Start(ctx); err != nil { + return err + } + + return nil +} + +// Snapshot collects data on the current state of the system to populate user space and kernel space caches. +func (r *EBPFLessResolvers) Snapshot() error { + return nil +} + +// Close cleans up any underlying resolver that requires a cleanup +func (r *EBPFLessResolvers) Close() error { + return nil +} diff --git a/pkg/security/resolvers/resolvers_others.go b/pkg/security/resolvers/resolvers_others.go deleted file mode 100644 index 54635c1aa1582..0000000000000 --- a/pkg/security/resolvers/resolvers_others.go +++ /dev/null @@ -1,24 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016-present Datadog, Inc. - -//go:build !linux && !windows - -// Package resolvers holds resolvers related files -package resolvers - -import ( - "fmt" - "github.com/DataDog/datadog-agent/pkg/security/config" - "github.com/DataDog/datadog-go/v5/statsd" -) - -// Resolvers holds the list of the event attribute resolvers -type Resolvers struct { -} - -// NewResolvers creates a new instance of Resolvers -func NewResolvers(config *config.Config, statsdClient statsd.ClientInterface) (*Resolvers, error) { - return nil, fmt.Errorf("Not implemented on this platform") -} diff --git a/pkg/security/secl/compiler/generators/accessors/accessors.go b/pkg/security/secl/compiler/generators/accessors/accessors.go index 0fa91a6a5458a..32595b8790657 100644 --- a/pkg/security/secl/compiler/generators/accessors/accessors.go +++ b/pkg/security/secl/compiler/generators/accessors/accessors.go @@ -26,6 +26,7 @@ import ( "github.com/Masterminds/sprig/v3" "github.com/davecgh/go-spew/spew" "github.com/fatih/structtag" + "golang.org/x/exp/slices" "golang.org/x/text/cases" "golang.org/x/text/language" "golang.org/x/tools/go/packages" @@ -103,6 +104,10 @@ func origTypeToBasicType(kind string) string { return kind } +func isNetType(kind string) bool { + return kind == "net.IPNet" +} + func isBasicType(kind string) bool { switch kind { case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "net.IPNet": @@ -112,8 +117,12 @@ func isBasicType(kind string) bool { } func isBasicTypeForGettersOnly(kind string) bool { + if isBasicType(kind) { + return true + } + switch kind { - case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "net.IPNet", "time.Time": + case "time.Time": return true } return false @@ -156,13 +165,10 @@ func handleBasic(module *common.Module, field seclField, name, alias, aliasPrefi Struct: containerStructName, Alias: alias, AliasPrefix: aliasPrefix, + GettersOnly: field.gettersOnly, } - if field.gettersOnly { - module.GettersOnlyFields[alias] = newStructField - } else { - module.Fields[alias] = newStructField - } + module.Fields[alias] = newStructField if _, ok := module.EventTypes[event]; !ok { module.EventTypes[event] = common.NewEventTypeMetada() @@ -187,13 +193,10 @@ func handleBasic(module *common.Module, field seclField, name, alias, aliasPrefi Struct: "string", Alias: alias, AliasPrefix: aliasPrefix, + GettersOnly: field.gettersOnly, } - if field.gettersOnly { - module.GettersOnlyFields[alias] = newStructField - } else { - module.Fields[alias] = newStructField - } + module.Fields[alias] = newStructField } } @@ -288,13 +291,10 @@ func handleFieldWithHandler(module *common.Module, field seclField, aliasPrefix, Check: field.check, Alias: alias, AliasPrefix: aliasPrefix, + GettersOnly: field.gettersOnly, } - if field.gettersOnly { - module.GettersOnlyFields[alias] = newStructField - } else { - module.Fields[alias] = newStructField - } + module.Fields[alias] = newStructField if field.lengthField { var lengthField = *module.Fields[alias] @@ -529,6 +529,12 @@ func handleSpecRecursive(module *common.Module, astFiles *AstFiles, spec interfa continue } + if isNetType((fieldType)) { + if !slices.Contains(module.Imports, "net") { + module.Imports = append(module.Imports, "net") + } + } + alias := seclField.name if isBasicType(fieldType) { handleBasic(module, seclField, fieldBasename, alias, aliasPrefix, prefix, fieldType, event, opOverrides, fieldCommentText, seclField.containerStructName, fieldIterator, isArray) @@ -563,7 +569,6 @@ func handleSpecRecursive(module *common.Module, astFiles *AstFiles, spec interfa } if handler := seclField.handler; handler != "" { - handleFieldWithHandler(module, seclField, aliasPrefix, prefix, prefixedFieldName, fieldType, seclField.containerStructName, event, fieldCommentText, opOverrides, handler, isPointer, isArray, fieldIterator) delete(dejavu, fieldBasename) @@ -682,15 +687,14 @@ func parseFile(modelFile string, typesFile string, pkgName string) (*common.Modu } module := &common.Module{ - Name: moduleName, - SourcePkg: pkgName, - TargetPkg: pkgName, - BuildTags: formatBuildTags(buildTags), - Fields: make(map[string]*common.StructField), - GettersOnlyFields: make(map[string]*common.StructField), - AllFields: make(map[string]*common.StructField), - Iterators: make(map[string]*common.StructField), - EventTypes: make(map[string]*common.EventTypeMetadata), + Name: moduleName, + SourcePkg: pkgName, + TargetPkg: pkgName, + BuildTags: formatBuildTags(buildTags), + Fields: make(map[string]*common.StructField), + AllFields: make(map[string]*common.StructField), + Iterators: make(map[string]*common.StructField), + EventTypes: make(map[string]*common.EventTypeMetadata), } // If the target package is different from the model package @@ -835,25 +839,6 @@ func needScrubbed(fieldName string) bool { return false } -func needFiltered(fieldName string) bool { - loweredFieldName := strings.ToLower(fieldName) - if strings.Contains(loweredFieldName, "env") && !strings.Contains(loweredFieldName, "truncated") { - return true - } - return false -} - -func combineFieldMaps(map1 map[string]*common.StructField, map2 map[string]*common.StructField) map[string]*common.StructField { - combined := make(map[string]*common.StructField) - for k, v := range map1 { - combined[k] = v - } - for key, value := range map2 { - combined[key] = value - } - return combined -} - func addSuffixToFuncPrototype(suffix string, prototype string) string { chunks := strings.SplitN(prototype, "(", 3) chunks = append(chunks[:1], append([]string{suffix, "("}, chunks[1:]...)...) @@ -977,8 +962,6 @@ var funcMap = map[string]interface{}{ "PascalCaseFieldName": pascalCaseFieldName, "GetDefaultValueOfType": getDefaultValueOfType, "NeedScrubbed": needScrubbed, - "NeedFiltered": needFiltered, - "CombineFieldMaps": combineFieldMaps, "AddSuffixToFuncPrototype": addSuffixToFuncPrototype, } @@ -1076,7 +1059,7 @@ func removeEmptyLines(input *bytes.Buffer) string { func init() { flag.BoolVar(&verbose, "verbose", false, "Be verbose") - flag.StringVar(&docOutput, "doc", "../../../../docs/cloud-workload-security/secl.json", "Generate documentation JSON") + flag.StringVar(&docOutput, "doc", "", "Generate documentation JSON") flag.StringVar(&fieldHandlersOutput, "field-handlers", "field_handlers_unix.go", "Field handlers output file") flag.StringVar(&modelFile, "input", os.Getenv("GOFILE"), "Go file to generate decoders from") flag.StringVar(&typesFile, "types-file", os.Getenv("TYPESFILE"), "Go type file to use with the model file") diff --git a/pkg/security/secl/compiler/generators/accessors/accessors.tmpl b/pkg/security/secl/compiler/generators/accessors/accessors.tmpl index a9f04a0f6606a..f7df03d7b3ac9 100644 --- a/pkg/security/secl/compiler/generators/accessors/accessors.tmpl +++ b/pkg/security/secl/compiler/generators/accessors/accessors.tmpl @@ -9,16 +9,15 @@ package {{.Name}} import ( - "net" - "reflect" + {{range .Imports }} + "{{.}}" + {{end}} + "reflect" {{if ne $.SourcePkg $.TargetPkg}}"{{.SourcePkg}}"{{end}} "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" ) -// Aliases used to avoid compilation error in case of unused imported package -type NetIP = net.IP - func (m *Model) GetIterator(field eval.Field) (eval.Iterator, error) { switch field { {{range $Name, $Field := .Iterators}} @@ -43,6 +42,10 @@ func (m *Model) GetEventTypes() []eval.EventType { func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Evaluator, error) { switch field { {{range $Name, $Field := .Fields}} + {{- if $Field.GettersOnly }} + {{continue}} + {{end}} + case "{{$Name}}": return &{{$Field.GetEvaluatorType}}{ {{- if $Field.OpOverrides}} @@ -195,6 +198,10 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval func (ev *Event) GetFields() []eval.Field { return []eval.Field{ {{range $Name, $Field := .Fields}} + {{- if $Field.GettersOnly }} + {{continue}} + {{end}} + "{{$Name}}", {{end}} } @@ -203,6 +210,10 @@ func (ev *Event) GetFields() []eval.Field { func (ev *Event) GetFieldValue(field eval.Field) (interface{}, error) { switch field { {{range $Name, $Field := .Fields}} + {{- if $Field.GettersOnly }} + {{continue}} + {{end}} + case "{{$Name}}": {{if $Field.Iterator}} var values []{{$Field.ReturnType}} @@ -313,6 +324,10 @@ func (ev *Event) GetFieldValue(field eval.Field) (interface{}, error) { func (ev *Event) GetFieldEventType(field eval.Field) (eval.EventType, error) { switch field { {{range $Name, $Field := .Fields}} + {{- if $Field.GettersOnly }} + {{continue}} + {{end}} + case "{{$Name}}": return "{{$Field.Event}}", nil {{end}} @@ -324,6 +339,9 @@ func (ev *Event) GetFieldEventType(field eval.Field) (eval.EventType, error) { func (ev *Event) GetFieldType(field eval.Field) (reflect.Kind, error) { switch field { {{range $Name, $Field := .Fields}} + {{- if $Field.GettersOnly }} + {{continue}} + {{end}} case "{{$Name}}": {{if eq $Field.ReturnType "string"}} @@ -344,6 +362,10 @@ func (ev *Event) GetFieldType(field eval.Field) (reflect.Kind, error) { func (ev *Event) SetFieldValue(field eval.Field, value interface{}) error { switch field { {{range $Name, $Field := .Fields}} + {{- if $Field.GettersOnly }} + {{continue}} + {{end}} + {{$FieldName := $Field.Name | printf "ev.%s"}} case "{{$Name}}": {{- $Field | NewField $.AllFields}} diff --git a/pkg/security/secl/compiler/generators/accessors/common/types.go b/pkg/security/secl/compiler/generators/accessors/common/types.go index d78b4c9f5c816..ce34fe4a041ed 100644 --- a/pkg/security/secl/compiler/generators/accessors/common/types.go +++ b/pkg/security/secl/compiler/generators/accessors/common/types.go @@ -30,17 +30,18 @@ const ( // Module represents everything needed to generate the accessors for a specific module (fields, build tags, ...) type Module struct { - Name string - SourcePkgPrefix string - SourcePkg string - TargetPkg string - BuildTags []string - Fields map[string]*StructField // Fields only contains fields that are exposed in SECL - GettersOnlyFields map[string]*StructField // GettersOnlyFields only contains fields that have generated getters but are not exposed in SECL - AllFields map[string]*StructField - Iterators map[string]*StructField - EventTypes map[string]*EventTypeMetadata - Mock bool + Name string + SourcePkgPrefix string + SourcePkg string + TargetPkg string + BuildTags []string + Fields map[string]*StructField // Fields only contains fields that are exposed in SECL + //GettersOnlyFields map[string]*StructField // GettersOnlyFields only contains fields that have generated getters but are not exposed in SECL + AllFields map[string]*StructField + Iterators map[string]*StructField + EventTypes map[string]*EventTypeMetadata + Mock bool + Imports []string } // StructField represents a structure field for which an accessor will be generated @@ -65,6 +66,7 @@ type StructField struct { Check string Alias string AliasPrefix string + GettersOnly bool } // GetEvaluatorType returns the evaluator type name diff --git a/pkg/security/secl/compiler/generators/accessors/doc/doc.go b/pkg/security/secl/compiler/generators/accessors/doc/doc.go index 182307a5583d0..29fac0eb8dc77 100644 --- a/pkg/security/secl/compiler/generators/accessors/doc/doc.go +++ b/pkg/security/secl/compiler/generators/accessors/doc/doc.go @@ -97,6 +97,10 @@ func GenerateDocJSON(module *common.Module, seclModelPath, outputPath string) er cachedDocumentation := make(map[string]*propertyDocumentation) for name, field := range module.Fields { + if field.GettersOnly { + continue + } + var propertyKey string var propertySuffix string var propertyDefinition string diff --git a/pkg/security/secl/compiler/generators/accessors/field_accessors.tmpl b/pkg/security/secl/compiler/generators/accessors/field_accessors.tmpl index 785d738f3c7d6..eade58343fb69 100644 --- a/pkg/security/secl/compiler/generators/accessors/field_accessors.tmpl +++ b/pkg/security/secl/compiler/generators/accessors/field_accessors.tmpl @@ -9,15 +9,15 @@ package {{.Name}} import ( - "net" + {{range .Imports }} + "{{.}}" + {{end}} "time" "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" ) -{{$combinedFieldMaps := CombineFieldMaps .Fields .GettersOnlyFields}} - -{{range $Name, $Field := $combinedFieldMaps}} +{{range $Name, $Field := .Fields}} {{ $pascalCaseName := PascalCaseFieldName $Name }} @@ -31,7 +31,7 @@ import ( {{ end }} // Get{{$pascalCaseName}} returns the value of the field, resolving if necessary -func (ev *Event) Get{{$pascalCaseName}}{{if NeedFiltered $Field.Handler }}(desiredKeys map[string]bool){{else}}(){{end}} {{ $accessorReturnType }} { +func (ev *Event) Get{{$pascalCaseName}}() {{ $accessorReturnType }} { {{ if and (ne $Field.Handler "ResolveAsync") (and (ne $Field.Handler "ResolveEventTimestamp") (ne $Field.Handler "ResolveEventTime")) }} zeroValue := {{ GetDefaultValueOfType $accessorReturnType}} {{ end }} @@ -65,11 +65,7 @@ func (ev *Event) Get{{$pascalCaseName}}{{if NeedFiltered $Field.Handler }}(desir {{if $Field.Handler}} {{$SubName = $Field.Iterator.Name | TrimPrefix $Field.Prefix}} {{$Handler := $Field.Iterator.Name | TrimPrefix $Field.Handler}} - {{ if NeedScrubbed $Name }} - {{$Return = print "ev.FieldHandlers." $Handler "Scrubbed(ev, &element" $SubName ")"}} - {{ else }} - {{$Return = print "ev.FieldHandlers." $Handler "(ev, &element" $SubName ")"}} - {{ end }} + {{$Return = print "ev.FieldHandlers." $Handler "(ev, &element" $SubName ")"}} {{end}} {{if $Field.IsLength}} @@ -89,9 +85,6 @@ func (ev *Event) Get{{$pascalCaseName}}{{if NeedFiltered $Field.Handler }}(desir {{if not $Field.GetArrayPrefix}} values = append(values, result) {{else}} - {{if NeedFiltered $Field.Handler }} - result = filterEnvs(result, desiredKeys) - {{ end }} values = append(values, result...) {{end}} @@ -123,28 +116,16 @@ func (ev *Event) Get{{$pascalCaseName}}{{if NeedFiltered $Field.Handler }}(desir {{end}} {{$Prefix := $Field.Prefix}} - {{ if or (eq $Field.Handler "ResolveEventTime") (not $Prefix) }} + {{ if not $Prefix }} {{$Return = print "ev.FieldHandlers." $Field.Handler "(ev)"}} {{- else if $Field.IsLength }} {{$Return = print "len(ev.FieldHandlers." $Field.Handler "(ev, " $Ptr "ev." $Prefix "))"}} - {{- else if NeedScrubbed $Field.Handler }} - {{$Return = print "ev.FieldHandlers." $Field.Handler "Scrubbed(ev, " $Ptr "ev." $Prefix ")"}} {{ else }} {{$Return = print "ev.FieldHandlers." $Field.Handler "(ev, " $Ptr "ev." $Prefix ")"}} {{end}} {{end}} - {{ if ($Field.IsArray) }} - resolvedField := {{$Return}} - {{if NeedFiltered $Field.Handler }} - resolvedField = filterEnvs(resolvedField, desiredKeys) - {{ end }} - fieldCopy := make({{$accessorReturnType}}, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy - {{ else }} - return {{$Return}} - {{ end }} + return {{$Return}} {{end}} } diff --git a/pkg/security/secl/compiler/generators/accessors/field_handlers.tmpl b/pkg/security/secl/compiler/generators/accessors/field_handlers.tmpl index 53df4b621f615..a91e393171f64 100644 --- a/pkg/security/secl/compiler/generators/accessors/field_handlers.tmpl +++ b/pkg/security/secl/compiler/generators/accessors/field_handlers.tmpl @@ -8,6 +8,10 @@ package {{.Name}} +import ( + "time" +) + // ResolveFields resolves all the fields associate to the event type. Context fields are automatically resolved. func (ev *Event) ResolveFields() { ev.resolveFields(false) @@ -22,6 +26,10 @@ func (ev *Event) resolveFields(forADs bool) { {{ $uniqueResolvers := dict }} // resolve context fields that are not related to any event type {{- range $Key, $Field := .Fields}} + {{- if $Field.GettersOnly }} + {{continue}} + {{end}} + {{- if and (eq $Field.Event "*") }} {{ $resolver := $Field | GetFieldHandler $.AllFields }} {{ if and (ne $resolver "") (not (hasKey $uniqueResolvers $resolver)) }} @@ -89,12 +97,7 @@ func (ev *Event) resolveFields(forADs bool) { type FieldHandlers interface { {{$Handlers := .Fields | GetHandlers}} {{range $Proto, $Impl := $Handlers}} - {{ if NeedScrubbed $Proto }} - {{$Proto}} - {{$Proto | AddSuffixToFuncPrototype "Scrubbed"}} - {{else}} - {{$Proto}} - {{ end }} + {{$Proto}} {{end}} // custom handlers not tied to any fields @@ -105,10 +108,5 @@ type DefaultFieldHandlers struct {} {{$Handlers := .Fields | GetHandlers}} {{range $Proto, $Impl := $Handlers}} - {{ if NeedScrubbed $Proto }} - func (dfh *DefaultFieldHandlers) {{$Proto}} {{$Impl}} - func (dfh *DefaultFieldHandlers) {{$Proto | AddSuffixToFuncPrototype "Scrubbed"}} {{$Impl}} - {{else}} - func (dfh *DefaultFieldHandlers) {{$Proto}} {{$Impl}} - {{ end }} + func (dfh *DefaultFieldHandlers) {{$Proto}} {{$Impl}} {{end}} diff --git a/pkg/security/secl/model/accessors_unix.go b/pkg/security/secl/model/accessors_unix.go index 4017dc67fe9e3..6c6095403ceef 100644 --- a/pkg/security/secl/model/accessors_unix.go +++ b/pkg/security/secl/model/accessors_unix.go @@ -15,9 +15,6 @@ import ( "reflect" ) -// Aliases used to avoid compilation error in case of unused imported package -type NetIP = net.IP - func (m *Model) GetIterator(field eval.Field) (eval.Iterator, error) { switch field { case "process.ancestors": @@ -759,7 +756,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgs(ev, ev.Exec.Process) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "exec.args_flags": return &eval.StringArrayEvaluator{ @@ -795,7 +792,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgv(ev, ev.Exec.Process) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "exec.argv0": return &eval.StringEvaluator{ @@ -1550,7 +1547,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgs(ev, ev.Exit.Process) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "exit.args_flags": return &eval.StringArrayEvaluator{ @@ -1586,7 +1583,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgv(ev, ev.Exit.Process) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "exit.argv0": return &eval.StringEvaluator{ @@ -3416,6 +3413,15 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval Field: field, Weight: eval.FunctionWeight, }, nil + case "mount.root.path": + return &eval.StringEvaluator{ + EvalFnc: func(ctx *eval.Context) string { + ev := ctx.Event.(*Event) + return ev.FieldHandlers.ResolveMountRootPath(ev, &ev.Mount) + }, + Field: field, + Weight: eval.HandlerWeight, + }, nil case "mount.source.path": return &eval.StringEvaluator{ EvalFnc: func(ctx *eval.Context) string { @@ -3763,7 +3769,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval ctx.StringCache[field] = results return results }, Field: field, - Weight: 100 * eval.IteratorWeight, + Weight: 500 * eval.IteratorWeight, }, nil case "process.ancestors.args_flags": return &eval.StringArrayEvaluator{ @@ -3847,7 +3853,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval ctx.StringCache[field] = results return results }, Field: field, - Weight: 100 * eval.IteratorWeight, + Weight: 500 * eval.IteratorWeight, }, nil case "process.ancestors.argv0": return &eval.StringArrayEvaluator{ @@ -5478,7 +5484,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgs(ev, &ev.BaseEvent.ProcessContext.Process) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "process.args_flags": return &eval.StringArrayEvaluator{ @@ -5514,7 +5520,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgv(ev, &ev.BaseEvent.ProcessContext.Process) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "process.argv0": return &eval.StringEvaluator{ @@ -6191,7 +6197,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgs(ev, ev.BaseEvent.ProcessContext.Parent) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "process.parent.args_flags": return &eval.StringArrayEvaluator{ @@ -6239,7 +6245,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgv(ev, ev.BaseEvent.ProcessContext.Parent) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "process.parent.argv0": return &eval.StringEvaluator{ @@ -7303,7 +7309,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval ctx.StringCache[field] = results return results }, Field: field, - Weight: 100 * eval.IteratorWeight, + Weight: 500 * eval.IteratorWeight, }, nil case "ptrace.tracee.ancestors.args_flags": return &eval.StringArrayEvaluator{ @@ -7387,7 +7393,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval ctx.StringCache[field] = results return results }, Field: field, - Weight: 100 * eval.IteratorWeight, + Weight: 500 * eval.IteratorWeight, }, nil case "ptrace.tracee.ancestors.argv0": return &eval.StringArrayEvaluator{ @@ -9018,7 +9024,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgs(ev, &ev.PTrace.Tracee.Process) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "ptrace.tracee.args_flags": return &eval.StringArrayEvaluator{ @@ -9054,7 +9060,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgv(ev, &ev.PTrace.Tracee.Process) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "ptrace.tracee.argv0": return &eval.StringEvaluator{ @@ -9731,7 +9737,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgs(ev, ev.PTrace.Tracee.Parent) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "ptrace.tracee.parent.args_flags": return &eval.StringArrayEvaluator{ @@ -9779,7 +9785,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgv(ev, ev.PTrace.Tracee.Parent) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "ptrace.tracee.parent.argv0": return &eval.StringEvaluator{ @@ -11979,7 +11985,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval ctx.StringCache[field] = results return results }, Field: field, - Weight: 100 * eval.IteratorWeight, + Weight: 500 * eval.IteratorWeight, }, nil case "signal.target.ancestors.args_flags": return &eval.StringArrayEvaluator{ @@ -12063,7 +12069,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval ctx.StringCache[field] = results return results }, Field: field, - Weight: 100 * eval.IteratorWeight, + Weight: 500 * eval.IteratorWeight, }, nil case "signal.target.ancestors.argv0": return &eval.StringArrayEvaluator{ @@ -13694,7 +13700,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgs(ev, &ev.Signal.Target.Process) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "signal.target.args_flags": return &eval.StringArrayEvaluator{ @@ -13730,7 +13736,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgv(ev, &ev.Signal.Target.Process) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "signal.target.argv0": return &eval.StringEvaluator{ @@ -14407,7 +14413,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgs(ev, ev.Signal.Target.Parent) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "signal.target.parent.args_flags": return &eval.StringArrayEvaluator{ @@ -14455,7 +14461,7 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return ev.FieldHandlers.ResolveProcessArgv(ev, ev.Signal.Target.Parent) }, Field: field, - Weight: 100 * eval.HandlerWeight, + Weight: 500 * eval.HandlerWeight, }, nil case "signal.target.parent.argv0": return &eval.StringEvaluator{ @@ -16463,6 +16469,7 @@ func (ev *Event) GetFields() []eval.Field { "mount.fs_type", "mount.mountpoint.path", "mount.retval", + "mount.root.path", "mount.source.path", "mprotect.req_protection", "mprotect.retval", @@ -18281,6 +18288,8 @@ func (ev *Event) GetFieldValue(field eval.Field) (interface{}, error) { return ev.FieldHandlers.ResolveMountPointPath(ev, &ev.Mount), nil case "mount.retval": return int(ev.Mount.SyscallEvent.Retval), nil + case "mount.root.path": + return ev.FieldHandlers.ResolveMountRootPath(ev, &ev.Mount), nil case "mount.source.path": return ev.FieldHandlers.ResolveMountSourcePath(ev, &ev.Mount), nil case "mprotect.req_protection": @@ -24325,6 +24334,8 @@ func (ev *Event) GetFieldEventType(field eval.Field) (eval.EventType, error) { return "mount", nil case "mount.retval": return "mount", nil + case "mount.root.path": + return "mount", nil case "mount.source.path": return "mount", nil case "mprotect.req_protection": @@ -26832,6 +26843,8 @@ func (ev *Event) GetFieldType(field eval.Field) (reflect.Kind, error) { return reflect.String, nil case "mount.retval": return reflect.Int, nil + case "mount.root.path": + return reflect.String, nil case "mount.source.path": return reflect.String, nil case "mprotect.req_protection": @@ -31478,6 +31491,13 @@ func (ev *Event) SetFieldValue(field eval.Field, value interface{}) error { } ev.Mount.SyscallEvent.Retval = int64(rv) return nil + case "mount.root.path": + rv, ok := value.(string) + if !ok { + return &eval.ErrValueTypeMismatch{Field: "Mount.MountRootPath"} + } + ev.Mount.MountRootPath = rv + return nil case "mount.source.path": rv, ok := value.(string) if !ok { diff --git a/pkg/security/secl/model/accessors_windows.go b/pkg/security/secl/model/accessors_windows.go index 6eb08c38aa01f..855cbb4aa786b 100644 --- a/pkg/security/secl/model/accessors_windows.go +++ b/pkg/security/secl/model/accessors_windows.go @@ -11,13 +11,9 @@ package model import ( "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" - "net" "reflect" ) -// Aliases used to avoid compilation error in case of unused imported package -type NetIP = net.IP - func (m *Model) GetIterator(field eval.Field) (eval.Iterator, error) { switch field { case "process.ancestors": @@ -27,7 +23,6 @@ func (m *Model) GetIterator(field eval.Field) (eval.Iterator, error) { } func (m *Model) GetEventTypes() []eval.EventType { return []eval.EventType{ - eval.EventType("dns"), eval.EventType("exec"), eval.EventType("exit"), } @@ -74,10 +69,10 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return &eval.StringEvaluator{ EvalFnc: func(ctx *eval.Context) string { ev := ctx.Event.(*Event) - return ev.Exec.Process.CmdLine + return ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.Exec.Process) }, Field: field, - Weight: eval.FunctionWeight, + Weight: 200 * eval.HandlerWeight, }, nil case "exec.container.id": return &eval.StringEvaluator{ @@ -182,10 +177,10 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return &eval.StringEvaluator{ EvalFnc: func(ctx *eval.Context) string { ev := ctx.Event.(*Event) - return ev.Exit.Process.CmdLine + return ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.Exit.Process) }, Field: field, - Weight: eval.FunctionWeight, + Weight: 200 * eval.HandlerWeight, }, nil case "exit.code": return &eval.IntEvaluator{ @@ -286,72 +281,10 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval Field: field, Weight: eval.FunctionWeight, }, nil - case "network.destination.ip": - return &eval.CIDREvaluator{ - EvalFnc: func(ctx *eval.Context) net.IPNet { - ev := ctx.Event.(*Event) - return ev.BaseEvent.NetworkContext.Destination.IPNet - }, - Field: field, - Weight: eval.FunctionWeight, - }, nil - case "network.destination.port": - return &eval.IntEvaluator{ - EvalFnc: func(ctx *eval.Context) int { - ev := ctx.Event.(*Event) - return int(ev.BaseEvent.NetworkContext.Destination.Port) - }, - Field: field, - Weight: eval.FunctionWeight, - }, nil - case "network.l3_protocol": - return &eval.IntEvaluator{ - EvalFnc: func(ctx *eval.Context) int { - ev := ctx.Event.(*Event) - return int(ev.BaseEvent.NetworkContext.L3Protocol) - }, - Field: field, - Weight: eval.FunctionWeight, - }, nil - case "network.l4_protocol": - return &eval.IntEvaluator{ - EvalFnc: func(ctx *eval.Context) int { - ev := ctx.Event.(*Event) - return int(ev.BaseEvent.NetworkContext.L4Protocol) - }, - Field: field, - Weight: eval.FunctionWeight, - }, nil - case "network.size": - return &eval.IntEvaluator{ - EvalFnc: func(ctx *eval.Context) int { - ev := ctx.Event.(*Event) - return int(ev.BaseEvent.NetworkContext.Size) - }, - Field: field, - Weight: eval.FunctionWeight, - }, nil - case "network.source.ip": - return &eval.CIDREvaluator{ - EvalFnc: func(ctx *eval.Context) net.IPNet { - ev := ctx.Event.(*Event) - return ev.BaseEvent.NetworkContext.Source.IPNet - }, - Field: field, - Weight: eval.FunctionWeight, - }, nil - case "network.source.port": - return &eval.IntEvaluator{ - EvalFnc: func(ctx *eval.Context) int { - ev := ctx.Event.(*Event) - return int(ev.BaseEvent.NetworkContext.Source.Port) - }, - Field: field, - Weight: eval.FunctionWeight, - }, nil case "process.ancestors.cmdline": return &eval.StringArrayEvaluator{ EvalFnc: func(ctx *eval.Context) []string { + ev := ctx.Event.(*Event) if result, ok := ctx.StringCache[field]; ok { return result } @@ -360,14 +293,14 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval value := iterator.Front(ctx) for value != nil { element := (*ProcessCacheEntry)(value) - result := element.ProcessContext.Process.CmdLine + result := ev.FieldHandlers.ResolveProcessCmdLine(ev, &element.ProcessContext.Process) results = append(results, result) value = iterator.Next() } ctx.StringCache[field] = results return results }, Field: field, - Weight: eval.IteratorWeight, + Weight: 200 * eval.IteratorWeight, }, nil case "process.ancestors.container.id": return &eval.StringArrayEvaluator{ @@ -580,10 +513,10 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval return &eval.StringEvaluator{ EvalFnc: func(ctx *eval.Context) string { ev := ctx.Event.(*Event) - return ev.BaseEvent.ProcessContext.Process.CmdLine + return ev.FieldHandlers.ResolveProcessCmdLine(ev, &ev.BaseEvent.ProcessContext.Process) }, Field: field, - Weight: eval.FunctionWeight, + Weight: 200 * eval.HandlerWeight, }, nil case "process.container.id": return &eval.StringEvaluator{ @@ -664,10 +597,10 @@ func (m *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval if !ev.BaseEvent.ProcessContext.HasParent() { return "" } - return ev.BaseEvent.ProcessContext.Parent.CmdLine + return ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.BaseEvent.ProcessContext.Parent) }, Field: field, - Weight: eval.FunctionWeight, + Weight: 200 * eval.HandlerWeight, }, nil case "process.parent.container.id": return &eval.StringEvaluator{ @@ -834,13 +767,6 @@ func (ev *Event) GetFields() []eval.Field { "exit.file.path.length", "exit.pid", "exit.ppid", - "network.destination.ip", - "network.destination.port", - "network.l3_protocol", - "network.l4_protocol", - "network.size", - "network.source.ip", - "network.source.port", "process.ancestors.cmdline", "process.ancestors.container.id", "process.ancestors.created_at", @@ -887,7 +813,7 @@ func (ev *Event) GetFieldValue(field eval.Field) (interface{}, error) { case "event.timestamp": return int(ev.FieldHandlers.ResolveEventTimestamp(ev, &ev.BaseEvent)), nil case "exec.cmdline": - return ev.Exec.Process.CmdLine, nil + return ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.Exec.Process), nil case "exec.container.id": return ev.Exec.Process.ContainerID, nil case "exec.created_at": @@ -911,7 +837,7 @@ func (ev *Event) GetFieldValue(field eval.Field) (interface{}, error) { case "exit.cause": return int(ev.Exit.Cause), nil case "exit.cmdline": - return ev.Exit.Process.CmdLine, nil + return ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.Exit.Process), nil case "exit.code": return int(ev.Exit.Code), nil case "exit.container.id": @@ -934,20 +860,6 @@ func (ev *Event) GetFieldValue(field eval.Field) (interface{}, error) { return int(ev.Exit.Process.PIDContext.Pid), nil case "exit.ppid": return int(ev.Exit.Process.PPid), nil - case "network.destination.ip": - return ev.BaseEvent.NetworkContext.Destination.IPNet, nil - case "network.destination.port": - return int(ev.BaseEvent.NetworkContext.Destination.Port), nil - case "network.l3_protocol": - return int(ev.BaseEvent.NetworkContext.L3Protocol), nil - case "network.l4_protocol": - return int(ev.BaseEvent.NetworkContext.L4Protocol), nil - case "network.size": - return int(ev.BaseEvent.NetworkContext.Size), nil - case "network.source.ip": - return ev.BaseEvent.NetworkContext.Source.IPNet, nil - case "network.source.port": - return int(ev.BaseEvent.NetworkContext.Source.Port), nil case "process.ancestors.cmdline": var values []string ctx := eval.NewContext(ev) @@ -955,7 +867,7 @@ func (ev *Event) GetFieldValue(field eval.Field) (interface{}, error) { ptr := iterator.Front(ctx) for ptr != nil { element := (*ProcessCacheEntry)(ptr) - result := element.ProcessContext.Process.CmdLine + result := ev.FieldHandlers.ResolveProcessCmdLine(ev, &element.ProcessContext.Process) values = append(values, result) ptr = iterator.Next() } @@ -1081,7 +993,7 @@ func (ev *Event) GetFieldValue(field eval.Field) (interface{}, error) { } return values, nil case "process.cmdline": - return ev.BaseEvent.ProcessContext.Process.CmdLine, nil + return ev.FieldHandlers.ResolveProcessCmdLine(ev, &ev.BaseEvent.ProcessContext.Process), nil case "process.container.id": return ev.BaseEvent.ProcessContext.Process.ContainerID, nil case "process.created_at": @@ -1102,7 +1014,7 @@ func (ev *Event) GetFieldValue(field eval.Field) (interface{}, error) { if !ev.BaseEvent.ProcessContext.HasParent() { return "", &eval.ErrNotSupported{Field: field} } - return ev.BaseEvent.ProcessContext.Parent.CmdLine, nil + return ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.BaseEvent.ProcessContext.Parent), nil case "process.parent.container.id": if !ev.BaseEvent.ProcessContext.HasParent() { return "", &eval.ErrNotSupported{Field: field} @@ -1212,20 +1124,6 @@ func (ev *Event) GetFieldEventType(field eval.Field) (eval.EventType, error) { return "exit", nil case "exit.ppid": return "exit", nil - case "network.destination.ip": - return "dns", nil - case "network.destination.port": - return "dns", nil - case "network.l3_protocol": - return "dns", nil - case "network.l4_protocol": - return "dns", nil - case "network.size": - return "dns", nil - case "network.source.ip": - return "dns", nil - case "network.source.port": - return "dns", nil case "process.ancestors.cmdline": return "*", nil case "process.ancestors.container.id": @@ -1353,20 +1251,6 @@ func (ev *Event) GetFieldType(field eval.Field) (reflect.Kind, error) { return reflect.Int, nil case "exit.ppid": return reflect.Int, nil - case "network.destination.ip": - return reflect.Struct, nil - case "network.destination.port": - return reflect.Int, nil - case "network.l3_protocol": - return reflect.Int, nil - case "network.l4_protocol": - return reflect.Int, nil - case "network.size": - return reflect.Int, nil - case "network.source.ip": - return reflect.Struct, nil - case "network.source.port": - return reflect.Int, nil case "process.ancestors.cmdline": return reflect.String, nil case "process.ancestors.container.id": @@ -1704,55 +1588,6 @@ func (ev *Event) SetFieldValue(field eval.Field, value interface{}) error { } ev.Exit.Process.PPid = uint32(rv) return nil - case "network.destination.ip": - rv, ok := value.(net.IPNet) - if !ok { - return &eval.ErrValueTypeMismatch{Field: "BaseEvent.NetworkContext.Destination.IPNet"} - } - ev.BaseEvent.NetworkContext.Destination.IPNet = rv - return nil - case "network.destination.port": - rv, ok := value.(int) - if !ok { - return &eval.ErrValueTypeMismatch{Field: "BaseEvent.NetworkContext.Destination.Port"} - } - ev.BaseEvent.NetworkContext.Destination.Port = uint16(rv) - return nil - case "network.l3_protocol": - rv, ok := value.(int) - if !ok { - return &eval.ErrValueTypeMismatch{Field: "BaseEvent.NetworkContext.L3Protocol"} - } - ev.BaseEvent.NetworkContext.L3Protocol = uint16(rv) - return nil - case "network.l4_protocol": - rv, ok := value.(int) - if !ok { - return &eval.ErrValueTypeMismatch{Field: "BaseEvent.NetworkContext.L4Protocol"} - } - ev.BaseEvent.NetworkContext.L4Protocol = uint16(rv) - return nil - case "network.size": - rv, ok := value.(int) - if !ok { - return &eval.ErrValueTypeMismatch{Field: "BaseEvent.NetworkContext.Size"} - } - ev.BaseEvent.NetworkContext.Size = uint32(rv) - return nil - case "network.source.ip": - rv, ok := value.(net.IPNet) - if !ok { - return &eval.ErrValueTypeMismatch{Field: "BaseEvent.NetworkContext.Source.IPNet"} - } - ev.BaseEvent.NetworkContext.Source.IPNet = rv - return nil - case "network.source.port": - rv, ok := value.(int) - if !ok { - return &eval.ErrValueTypeMismatch{Field: "BaseEvent.NetworkContext.Source.Port"} - } - ev.BaseEvent.NetworkContext.Source.Port = uint16(rv) - return nil case "process.ancestors.cmdline": if ev.BaseEvent.ProcessContext == nil { ev.BaseEvent.ProcessContext = &ProcessContext{} diff --git a/pkg/security/secl/model/args_envs.go b/pkg/security/secl/model/args_envs.go index 1c697af19f401..9de6ab1a43b21 100644 --- a/pkg/security/secl/model/args_envs.go +++ b/pkg/security/secl/model/args_envs.go @@ -79,9 +79,7 @@ func (p *EnvsEntry) FilterEnvs(envsWithValue map[string]bool) ([]string, bool) { } // FilterEnvs returns an array of environment variable key value pairs matching the desired keys -// -//nolint:unused -func filterEnvs(allEnvVars []string, desiredKeys map[string]bool) []string { +func FilterEnvs(allEnvVars []string, desiredKeys map[string]bool) []string { if len(allEnvVars) == 0 { return nil } diff --git a/pkg/security/secl/model/field_accessors_unix.go b/pkg/security/secl/model/field_accessors_unix.go index 2a20f6a292aa5..d091b71b2bbb4 100644 --- a/pkg/security/secl/model/field_accessors_unix.go +++ b/pkg/security/secl/model/field_accessors_unix.go @@ -93,10 +93,7 @@ func (ev *Event) GetBpfProgHelpers() []uint32 { if ev.GetEventType().String() != "bpf" { return zeroValue } - resolvedField := ev.BPF.Program.Helpers - fieldCopy := make([]uint32, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.BPF.Program.Helpers } // GetBpfProgName returns the value of the field, resolving if necessary @@ -213,10 +210,7 @@ func (ev *Event) GetChmodFileHashes() []string { if ev.GetEventType().String() != "chmod" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Chmod.File) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Chmod.File) } // GetChmodFileInUpperLayer returns the value of the field, resolving if necessary @@ -441,10 +435,7 @@ func (ev *Event) GetChownFileHashes() []string { if ev.GetEventType().String() != "chown" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Chown.File) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Chown.File) } // GetChownFileInUpperLayer returns the value of the field, resolving if necessary @@ -615,10 +606,7 @@ func (ev *Event) GetContainerTags() []string { if ev.BaseEvent.ContainerContext == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveContainerTags(ev, ev.BaseEvent.ContainerContext) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveContainerTags(ev, ev.BaseEvent.ContainerContext) } // GetDnsId returns the value of the field, resolving if necessary @@ -715,10 +703,7 @@ func (ev *Event) GetExecArgsFlags() []string { if ev.Exec.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgsFlags(ev, ev.Exec.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsFlags(ev, ev.Exec.Process) } // GetExecArgsOptions returns the value of the field, resolving if necessary @@ -730,10 +715,19 @@ func (ev *Event) GetExecArgsOptions() []string { if ev.Exec.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgsOptions(ev, ev.Exec.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsOptions(ev, ev.Exec.Process) +} + +// GetExecArgsScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetExecArgsScrubbed() string { + zeroValue := "" + if ev.GetEventType().String() != "exec" { + return zeroValue + } + if ev.Exec.Process == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, ev.Exec.Process) } // GetExecArgsTruncated returns the value of the field, resolving if necessary @@ -757,10 +751,7 @@ func (ev *Event) GetExecArgv() []string { if ev.Exec.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.Exec.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgv(ev, ev.Exec.Process) } // GetExecArgv0 returns the value of the field, resolving if necessary @@ -775,6 +766,18 @@ func (ev *Event) GetExecArgv0() string { return ev.FieldHandlers.ResolveProcessArgv0(ev, ev.Exec.Process) } +// GetExecArgvScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetExecArgvScrubbed() []string { + zeroValue := []string{} + if ev.GetEventType().String() != "exec" { + return zeroValue + } + if ev.Exec.Process == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.Exec.Process) +} + // GetExecCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetExecCapEffective() uint64 { zeroValue := uint64(0) @@ -860,7 +863,7 @@ func (ev *Event) GetExecEgroup() string { } // GetExecEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetExecEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetExecEnvp() []string { zeroValue := []string{} if ev.GetEventType().String() != "exec" { return zeroValue @@ -868,15 +871,11 @@ func (ev *Event) GetExecEnvp(desiredKeys map[string]bool) []string { if ev.Exec.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exec.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exec.Process) } // GetExecEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetExecEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetExecEnvs() []string { zeroValue := []string{} if ev.GetEventType().String() != "exec" { return zeroValue @@ -884,11 +883,7 @@ func (ev *Event) GetExecEnvs(desiredKeys map[string]bool) []string { if ev.Exec.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exec.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exec.Process) } // GetExecEnvsTruncated returns the value of the field, resolving if necessary @@ -1023,10 +1018,7 @@ func (ev *Event) GetExecFileHashes() []string { if !ev.Exec.Process.IsNotKworker() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Exec.Process.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Exec.Process.FileEvent) } // GetExecFileInUpperLayer returns the value of the field, resolving if necessary @@ -1404,10 +1396,7 @@ func (ev *Event) GetExecInterpreterFileHashes() []string { if !ev.Exec.Process.HasInterpreter() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Exec.Process.LinuxBinprm.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Exec.Process.LinuxBinprm.FileEvent) } // GetExecInterpreterFileInUpperLayer returns the value of the field, resolving if necessary @@ -1734,10 +1723,7 @@ func (ev *Event) GetExecUserSessionK8sGroups() []string { if ev.Exec.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveK8SGroups(ev, &ev.Exec.Process.UserSession) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.Exec.Process.UserSession) } // GetExecUserSessionK8sUid returns the value of the field, resolving if necessary @@ -1785,10 +1771,7 @@ func (ev *Event) GetExitArgsFlags() []string { if ev.Exit.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgsFlags(ev, ev.Exit.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsFlags(ev, ev.Exit.Process) } // GetExitArgsOptions returns the value of the field, resolving if necessary @@ -1800,10 +1783,19 @@ func (ev *Event) GetExitArgsOptions() []string { if ev.Exit.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgsOptions(ev, ev.Exit.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsOptions(ev, ev.Exit.Process) +} + +// GetExitArgsScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetExitArgsScrubbed() string { + zeroValue := "" + if ev.GetEventType().String() != "exit" { + return zeroValue + } + if ev.Exit.Process == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, ev.Exit.Process) } // GetExitArgsTruncated returns the value of the field, resolving if necessary @@ -1827,10 +1819,7 @@ func (ev *Event) GetExitArgv() []string { if ev.Exit.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.Exit.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgv(ev, ev.Exit.Process) } // GetExitArgv0 returns the value of the field, resolving if necessary @@ -1845,6 +1834,18 @@ func (ev *Event) GetExitArgv0() string { return ev.FieldHandlers.ResolveProcessArgv0(ev, ev.Exit.Process) } +// GetExitArgvScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetExitArgvScrubbed() []string { + zeroValue := []string{} + if ev.GetEventType().String() != "exit" { + return zeroValue + } + if ev.Exit.Process == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.Exit.Process) +} + // GetExitCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetExitCapEffective() uint64 { zeroValue := uint64(0) @@ -1948,7 +1949,7 @@ func (ev *Event) GetExitEgroup() string { } // GetExitEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetExitEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetExitEnvp() []string { zeroValue := []string{} if ev.GetEventType().String() != "exit" { return zeroValue @@ -1956,15 +1957,11 @@ func (ev *Event) GetExitEnvp(desiredKeys map[string]bool) []string { if ev.Exit.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exit.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exit.Process) } // GetExitEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetExitEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetExitEnvs() []string { zeroValue := []string{} if ev.GetEventType().String() != "exit" { return zeroValue @@ -1972,11 +1969,7 @@ func (ev *Event) GetExitEnvs(desiredKeys map[string]bool) []string { if ev.Exit.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exit.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exit.Process) } // GetExitEnvsTruncated returns the value of the field, resolving if necessary @@ -2111,10 +2104,7 @@ func (ev *Event) GetExitFileHashes() []string { if !ev.Exit.Process.IsNotKworker() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Exit.Process.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Exit.Process.FileEvent) } // GetExitFileInUpperLayer returns the value of the field, resolving if necessary @@ -2492,10 +2482,7 @@ func (ev *Event) GetExitInterpreterFileHashes() []string { if !ev.Exit.Process.HasInterpreter() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Exit.Process.LinuxBinprm.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Exit.Process.LinuxBinprm.FileEvent) } // GetExitInterpreterFileInUpperLayer returns the value of the field, resolving if necessary @@ -2822,10 +2809,7 @@ func (ev *Event) GetExitUserSessionK8sGroups() []string { if ev.Exit.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveK8SGroups(ev, &ev.Exit.Process.UserSession) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.Exit.Process.UserSession) } // GetExitUserSessionK8sUid returns the value of the field, resolving if necessary @@ -2903,10 +2887,7 @@ func (ev *Event) GetLinkFileDestinationHashes() []string { if ev.GetEventType().String() != "link" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Link.Target) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Link.Target) } // GetLinkFileDestinationInUpperLayer returns the value of the field, resolving if necessary @@ -3077,10 +3058,7 @@ func (ev *Event) GetLinkFileHashes() []string { if ev.GetEventType().String() != "link" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Link.Source) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Link.Source) } // GetLinkFileInUpperLayer returns the value of the field, resolving if necessary @@ -3251,10 +3229,7 @@ func (ev *Event) GetLoadModuleArgv() []string { if ev.GetEventType().String() != "load_module" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveModuleArgv(ev, &ev.LoadModule) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveModuleArgv(ev, &ev.LoadModule) } // GetLoadModuleFileChangeTime returns the value of the field, resolving if necessary @@ -3299,10 +3274,7 @@ func (ev *Event) GetLoadModuleFileHashes() []string { if ev.GetEventType().String() != "load_module" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.LoadModule.File) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.LoadModule.File) } // GetLoadModuleFileInUpperLayer returns the value of the field, resolving if necessary @@ -3527,10 +3499,7 @@ func (ev *Event) GetMkdirFileHashes() []string { if ev.GetEventType().String() != "mkdir" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Mkdir.File) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Mkdir.File) } // GetMkdirFileInUpperLayer returns the value of the field, resolving if necessary @@ -3719,10 +3688,7 @@ func (ev *Event) GetMmapFileHashes() []string { if ev.GetEventType().String() != "mmap" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.MMap.File) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.MMap.File) } // GetMmapFileInUpperLayer returns the value of the field, resolving if necessary @@ -3914,6 +3880,15 @@ func (ev *Event) GetMountRetval() int64 { return ev.Mount.SyscallEvent.Retval } +// GetMountRootPath returns the value of the field, resolving if necessary +func (ev *Event) GetMountRootPath() string { + zeroValue := "" + if ev.GetEventType().String() != "mount" { + return zeroValue + } + return ev.FieldHandlers.ResolveMountRootPath(ev, &ev.Mount) +} + // GetMountSourcePath returns the value of the field, resolving if necessary func (ev *Event) GetMountSourcePath() string { zeroValue := "" @@ -4082,10 +4057,7 @@ func (ev *Event) GetOpenFileHashes() []string { if ev.GetEventType().String() != "open" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Open.File) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Open.File) } // GetOpenFileInUpperLayer returns the value of the field, resolving if necessary @@ -4307,6 +4279,28 @@ func (ev *Event) GetProcessAncestorsArgsOptions() []string { return values } +// GetProcessAncestorsArgsScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetProcessAncestorsArgsScrubbed() []string { + zeroValue := []string{} + if ev.BaseEvent.ProcessContext == nil { + return zeroValue + } + if ev.BaseEvent.ProcessContext.Ancestor == nil { + return zeroValue + } + var values []string + ctx := eval.NewContext(ev) + iterator := &ProcessAncestorsIterator{} + ptr := iterator.Front(ctx) + for ptr != nil { + element := (*ProcessCacheEntry)(ptr) + result := ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, &element.ProcessContext.Process) + values = append(values, result) + ptr = iterator.Next() + } + return values +} + // GetProcessAncestorsArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsArgsTruncated() []bool { zeroValue := []bool{} @@ -4344,7 +4338,7 @@ func (ev *Event) GetProcessAncestorsArgv() []string { ptr := iterator.Front(ctx) for ptr != nil { element := (*ProcessCacheEntry)(ptr) - result := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &element.ProcessContext.Process) + result := ev.FieldHandlers.ResolveProcessArgv(ev, &element.ProcessContext.Process) values = append(values, result...) ptr = iterator.Next() } @@ -4373,6 +4367,28 @@ func (ev *Event) GetProcessAncestorsArgv0() []string { return values } +// GetProcessAncestorsArgvScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetProcessAncestorsArgvScrubbed() []string { + zeroValue := []string{} + if ev.BaseEvent.ProcessContext == nil { + return zeroValue + } + if ev.BaseEvent.ProcessContext.Ancestor == nil { + return zeroValue + } + var values []string + ctx := eval.NewContext(ev) + iterator := &ProcessAncestorsIterator{} + ptr := iterator.Front(ctx) + for ptr != nil { + element := (*ProcessCacheEntry)(ptr) + result := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &element.ProcessContext.Process) + values = append(values, result...) + ptr = iterator.Next() + } + return values +} + // GetProcessAncestorsCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsCapEffective() []uint64 { zeroValue := []uint64{} @@ -4528,7 +4544,7 @@ func (ev *Event) GetProcessAncestorsEgroup() []string { } // GetProcessAncestorsEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetProcessAncestorsEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetProcessAncestorsEnvp() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue @@ -4543,7 +4559,6 @@ func (ev *Event) GetProcessAncestorsEnvp(desiredKeys map[string]bool) []string { for ptr != nil { element := (*ProcessCacheEntry)(ptr) result := ev.FieldHandlers.ResolveProcessEnvp(ev, &element.ProcessContext.Process) - result = filterEnvs(result, desiredKeys) values = append(values, result...) ptr = iterator.Next() } @@ -4551,7 +4566,7 @@ func (ev *Event) GetProcessAncestorsEnvp(desiredKeys map[string]bool) []string { } // GetProcessAncestorsEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetProcessAncestorsEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetProcessAncestorsEnvs() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue @@ -4566,7 +4581,6 @@ func (ev *Event) GetProcessAncestorsEnvs(desiredKeys map[string]bool) []string { for ptr != nil { element := (*ProcessCacheEntry)(ptr) result := ev.FieldHandlers.ResolveProcessEnvs(ev, &element.ProcessContext.Process) - result = filterEnvs(result, desiredKeys) values = append(values, result...) ptr = iterator.Next() } @@ -5908,10 +5922,7 @@ func (ev *Event) GetProcessArgsFlags() []string { if ev.BaseEvent.ProcessContext == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgsFlags(ev, &ev.BaseEvent.ProcessContext.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsFlags(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessArgsOptions returns the value of the field, resolving if necessary @@ -5920,10 +5931,16 @@ func (ev *Event) GetProcessArgsOptions() []string { if ev.BaseEvent.ProcessContext == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgsOptions(ev, &ev.BaseEvent.ProcessContext.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsOptions(ev, &ev.BaseEvent.ProcessContext.Process) +} + +// GetProcessArgsScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetProcessArgsScrubbed() string { + zeroValue := "" + if ev.BaseEvent.ProcessContext == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessArgsTruncated returns the value of the field, resolving if necessary @@ -5941,10 +5958,7 @@ func (ev *Event) GetProcessArgv() []string { if ev.BaseEvent.ProcessContext == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &ev.BaseEvent.ProcessContext.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgv(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessArgv0 returns the value of the field, resolving if necessary @@ -5956,6 +5970,15 @@ func (ev *Event) GetProcessArgv0() string { return ev.FieldHandlers.ResolveProcessArgv0(ev, &ev.BaseEvent.ProcessContext.Process) } +// GetProcessArgvScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetProcessArgvScrubbed() []string { + zeroValue := []string{} + if ev.BaseEvent.ProcessContext == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &ev.BaseEvent.ProcessContext.Process) +} + // GetProcessCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetProcessCapEffective() uint64 { zeroValue := uint64(0) @@ -6020,29 +6043,21 @@ func (ev *Event) GetProcessEgroup() string { } // GetProcessEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetProcessEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetProcessEnvp() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.BaseEvent.ProcessContext.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetProcessEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetProcessEnvs() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.BaseEvent.ProcessContext.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessEnvsTruncated returns the value of the field, resolving if necessary @@ -6147,10 +6162,7 @@ func (ev *Event) GetProcessFileHashes() []string { if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.BaseEvent.ProcessContext.Process.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.BaseEvent.ProcessContext.Process.FileEvent) } // GetProcessFileInUpperLayer returns the value of the field, resolving if necessary @@ -6447,10 +6459,7 @@ func (ev *Event) GetProcessInterpreterFileHashes() []string { if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.BaseEvent.ProcessContext.Process.LinuxBinprm.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.BaseEvent.ProcessContext.Process.LinuxBinprm.FileEvent) } // GetProcessInterpreterFileInUpperLayer returns the value of the field, resolving if necessary @@ -6672,10 +6681,7 @@ func (ev *Event) GetProcessParentArgsFlags() []string { if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessArgsFlags(ev, ev.BaseEvent.ProcessContext.Parent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsFlags(ev, ev.BaseEvent.ProcessContext.Parent) } // GetProcessParentArgsOptions returns the value of the field, resolving if necessary @@ -6690,10 +6696,22 @@ func (ev *Event) GetProcessParentArgsOptions() []string { if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessArgsOptions(ev, ev.BaseEvent.ProcessContext.Parent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsOptions(ev, ev.BaseEvent.ProcessContext.Parent) +} + +// GetProcessParentArgsScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetProcessParentArgsScrubbed() string { + zeroValue := "" + if ev.BaseEvent.ProcessContext == nil { + return zeroValue + } + if ev.BaseEvent.ProcessContext.Parent == nil { + return zeroValue + } + if !ev.BaseEvent.ProcessContext.HasParent() { + return "" + } + return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, ev.BaseEvent.ProcessContext.Parent) } // GetProcessParentArgsTruncated returns the value of the field, resolving if necessary @@ -6723,10 +6741,7 @@ func (ev *Event) GetProcessParentArgv() []string { if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.BaseEvent.ProcessContext.Parent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgv(ev, ev.BaseEvent.ProcessContext.Parent) } // GetProcessParentArgv0 returns the value of the field, resolving if necessary @@ -6744,6 +6759,21 @@ func (ev *Event) GetProcessParentArgv0() string { return ev.FieldHandlers.ResolveProcessArgv0(ev, ev.BaseEvent.ProcessContext.Parent) } +// GetProcessParentArgvScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetProcessParentArgvScrubbed() []string { + zeroValue := []string{} + if ev.BaseEvent.ProcessContext == nil { + return zeroValue + } + if ev.BaseEvent.ProcessContext.Parent == nil { + return zeroValue + } + if !ev.BaseEvent.ProcessContext.HasParent() { + return []string{} + } + return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.BaseEvent.ProcessContext.Parent) +} + // GetProcessParentCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentCapEffective() uint64 { zeroValue := uint64(0) @@ -6850,7 +6880,7 @@ func (ev *Event) GetProcessParentEgroup() string { } // GetProcessParentEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetProcessParentEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetProcessParentEnvp() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue @@ -6861,15 +6891,11 @@ func (ev *Event) GetProcessParentEnvp(desiredKeys map[string]bool) []string { if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessEnvp(ev, ev.BaseEvent.ProcessContext.Parent) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvp(ev, ev.BaseEvent.ProcessContext.Parent) } // GetProcessParentEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetProcessParentEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetProcessParentEnvs() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue @@ -6880,11 +6906,7 @@ func (ev *Event) GetProcessParentEnvs(desiredKeys map[string]bool) []string { if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessEnvs(ev, ev.BaseEvent.ProcessContext.Parent) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvs(ev, ev.BaseEvent.ProcessContext.Parent) } // GetProcessParentEnvsTruncated returns the value of the field, resolving if necessary @@ -7019,10 +7041,7 @@ func (ev *Event) GetProcessParentFileHashes() []string { if !ev.BaseEvent.ProcessContext.Parent.IsNotKworker() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.BaseEvent.ProcessContext.Parent.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.BaseEvent.ProcessContext.Parent.FileEvent) } // GetProcessParentFileInUpperLayer returns the value of the field, resolving if necessary @@ -7460,10 +7479,7 @@ func (ev *Event) GetProcessParentInterpreterFileHashes() []string { if !ev.BaseEvent.ProcessContext.Parent.HasInterpreter() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.BaseEvent.ProcessContext.Parent.LinuxBinprm.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.BaseEvent.ProcessContext.Parent.LinuxBinprm.FileEvent) } // GetProcessParentInterpreterFileInUpperLayer returns the value of the field, resolving if necessary @@ -7856,10 +7872,7 @@ func (ev *Event) GetProcessParentUserSessionK8sGroups() []string { if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveK8SGroups(ev, &ev.BaseEvent.ProcessContext.Parent.UserSession) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.BaseEvent.ProcessContext.Parent.UserSession) } // GetProcessParentUserSessionK8sUid returns the value of the field, resolving if necessary @@ -7952,10 +7965,7 @@ func (ev *Event) GetProcessUserSessionK8sGroups() []string { if ev.BaseEvent.ProcessContext == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveK8SGroups(ev, &ev.BaseEvent.ProcessContext.Process.UserSession) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.BaseEvent.ProcessContext.Process.UserSession) } // GetProcessUserSessionK8sUid returns the value of the field, resolving if necessary @@ -8069,6 +8079,31 @@ func (ev *Event) GetPtraceTraceeAncestorsArgsOptions() []string { return values } +// GetPtraceTraceeAncestorsArgsScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetPtraceTraceeAncestorsArgsScrubbed() []string { + zeroValue := []string{} + if ev.GetEventType().String() != "ptrace" { + return zeroValue + } + if ev.PTrace.Tracee == nil { + return zeroValue + } + if ev.PTrace.Tracee.Ancestor == nil { + return zeroValue + } + var values []string + ctx := eval.NewContext(ev) + iterator := &ProcessAncestorsIterator{} + ptr := iterator.Front(ctx) + for ptr != nil { + element := (*ProcessCacheEntry)(ptr) + result := ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, &element.ProcessContext.Process) + values = append(values, result) + ptr = iterator.Next() + } + return values +} + // GetPtraceTraceeAncestorsArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsArgsTruncated() []bool { zeroValue := []bool{} @@ -8112,7 +8147,7 @@ func (ev *Event) GetPtraceTraceeAncestorsArgv() []string { ptr := iterator.Front(ctx) for ptr != nil { element := (*ProcessCacheEntry)(ptr) - result := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &element.ProcessContext.Process) + result := ev.FieldHandlers.ResolveProcessArgv(ev, &element.ProcessContext.Process) values = append(values, result...) ptr = iterator.Next() } @@ -8144,6 +8179,31 @@ func (ev *Event) GetPtraceTraceeAncestorsArgv0() []string { return values } +// GetPtraceTraceeAncestorsArgvScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetPtraceTraceeAncestorsArgvScrubbed() []string { + zeroValue := []string{} + if ev.GetEventType().String() != "ptrace" { + return zeroValue + } + if ev.PTrace.Tracee == nil { + return zeroValue + } + if ev.PTrace.Tracee.Ancestor == nil { + return zeroValue + } + var values []string + ctx := eval.NewContext(ev) + iterator := &ProcessAncestorsIterator{} + ptr := iterator.Front(ctx) + for ptr != nil { + element := (*ProcessCacheEntry)(ptr) + result := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &element.ProcessContext.Process) + values = append(values, result...) + ptr = iterator.Next() + } + return values +} + // GetPtraceTraceeAncestorsCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsCapEffective() []uint64 { zeroValue := []uint64{} @@ -8320,7 +8380,7 @@ func (ev *Event) GetPtraceTraceeAncestorsEgroup() []string { } // GetPtraceTraceeAncestorsEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetPtraceTraceeAncestorsEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetPtraceTraceeAncestorsEnvp() []string { zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { return zeroValue @@ -8338,7 +8398,6 @@ func (ev *Event) GetPtraceTraceeAncestorsEnvp(desiredKeys map[string]bool) []str for ptr != nil { element := (*ProcessCacheEntry)(ptr) result := ev.FieldHandlers.ResolveProcessEnvp(ev, &element.ProcessContext.Process) - result = filterEnvs(result, desiredKeys) values = append(values, result...) ptr = iterator.Next() } @@ -8346,7 +8405,7 @@ func (ev *Event) GetPtraceTraceeAncestorsEnvp(desiredKeys map[string]bool) []str } // GetPtraceTraceeAncestorsEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetPtraceTraceeAncestorsEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetPtraceTraceeAncestorsEnvs() []string { zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { return zeroValue @@ -8364,7 +8423,6 @@ func (ev *Event) GetPtraceTraceeAncestorsEnvs(desiredKeys map[string]bool) []str for ptr != nil { element := (*ProcessCacheEntry)(ptr) result := ev.FieldHandlers.ResolveProcessEnvs(ev, &element.ProcessContext.Process) - result = filterEnvs(result, desiredKeys) values = append(values, result...) ptr = iterator.Next() } @@ -9892,10 +9950,7 @@ func (ev *Event) GetPtraceTraceeArgsFlags() []string { if ev.PTrace.Tracee == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgsFlags(ev, &ev.PTrace.Tracee.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsFlags(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeArgsOptions returns the value of the field, resolving if necessary @@ -9907,10 +9962,19 @@ func (ev *Event) GetPtraceTraceeArgsOptions() []string { if ev.PTrace.Tracee == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgsOptions(ev, &ev.PTrace.Tracee.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsOptions(ev, &ev.PTrace.Tracee.Process) +} + +// GetPtraceTraceeArgsScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetPtraceTraceeArgsScrubbed() string { + zeroValue := "" + if ev.GetEventType().String() != "ptrace" { + return zeroValue + } + if ev.PTrace.Tracee == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeArgsTruncated returns the value of the field, resolving if necessary @@ -9934,10 +9998,7 @@ func (ev *Event) GetPtraceTraceeArgv() []string { if ev.PTrace.Tracee == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &ev.PTrace.Tracee.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgv(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeArgv0 returns the value of the field, resolving if necessary @@ -9952,6 +10013,18 @@ func (ev *Event) GetPtraceTraceeArgv0() string { return ev.FieldHandlers.ResolveProcessArgv0(ev, &ev.PTrace.Tracee.Process) } +// GetPtraceTraceeArgvScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetPtraceTraceeArgvScrubbed() []string { + zeroValue := []string{} + if ev.GetEventType().String() != "ptrace" { + return zeroValue + } + if ev.PTrace.Tracee == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &ev.PTrace.Tracee.Process) +} + // GetPtraceTraceeCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeCapEffective() uint64 { zeroValue := uint64(0) @@ -10037,7 +10110,7 @@ func (ev *Event) GetPtraceTraceeEgroup() string { } // GetPtraceTraceeEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetPtraceTraceeEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetPtraceTraceeEnvp() []string { zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { return zeroValue @@ -10045,15 +10118,11 @@ func (ev *Event) GetPtraceTraceeEnvp(desiredKeys map[string]bool) []string { if ev.PTrace.Tracee == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.PTrace.Tracee.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetPtraceTraceeEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetPtraceTraceeEnvs() []string { zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { return zeroValue @@ -10061,11 +10130,7 @@ func (ev *Event) GetPtraceTraceeEnvs(desiredKeys map[string]bool) []string { if ev.PTrace.Tracee == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.PTrace.Tracee.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeEnvsTruncated returns the value of the field, resolving if necessary @@ -10200,10 +10265,7 @@ func (ev *Event) GetPtraceTraceeFileHashes() []string { if !ev.PTrace.Tracee.Process.IsNotKworker() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.PTrace.Tracee.Process.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.PTrace.Tracee.Process.FileEvent) } // GetPtraceTraceeFileInUpperLayer returns the value of the field, resolving if necessary @@ -10581,10 +10643,7 @@ func (ev *Event) GetPtraceTraceeInterpreterFileHashes() []string { if !ev.PTrace.Tracee.Process.HasInterpreter() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.PTrace.Tracee.Process.LinuxBinprm.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.PTrace.Tracee.Process.LinuxBinprm.FileEvent) } // GetPtraceTraceeInterpreterFileInUpperLayer returns the value of the field, resolving if necessary @@ -10863,10 +10922,7 @@ func (ev *Event) GetPtraceTraceeParentArgsFlags() []string { if !ev.PTrace.Tracee.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessArgsFlags(ev, ev.PTrace.Tracee.Parent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsFlags(ev, ev.PTrace.Tracee.Parent) } // GetPtraceTraceeParentArgsOptions returns the value of the field, resolving if necessary @@ -10884,10 +10940,25 @@ func (ev *Event) GetPtraceTraceeParentArgsOptions() []string { if !ev.PTrace.Tracee.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessArgsOptions(ev, ev.PTrace.Tracee.Parent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsOptions(ev, ev.PTrace.Tracee.Parent) +} + +// GetPtraceTraceeParentArgsScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetPtraceTraceeParentArgsScrubbed() string { + zeroValue := "" + if ev.GetEventType().String() != "ptrace" { + return zeroValue + } + if ev.PTrace.Tracee == nil { + return zeroValue + } + if ev.PTrace.Tracee.Parent == nil { + return zeroValue + } + if !ev.PTrace.Tracee.HasParent() { + return "" + } + return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, ev.PTrace.Tracee.Parent) } // GetPtraceTraceeParentArgsTruncated returns the value of the field, resolving if necessary @@ -10923,10 +10994,7 @@ func (ev *Event) GetPtraceTraceeParentArgv() []string { if !ev.PTrace.Tracee.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.PTrace.Tracee.Parent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgv(ev, ev.PTrace.Tracee.Parent) } // GetPtraceTraceeParentArgv0 returns the value of the field, resolving if necessary @@ -10947,6 +11015,24 @@ func (ev *Event) GetPtraceTraceeParentArgv0() string { return ev.FieldHandlers.ResolveProcessArgv0(ev, ev.PTrace.Tracee.Parent) } +// GetPtraceTraceeParentArgvScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetPtraceTraceeParentArgvScrubbed() []string { + zeroValue := []string{} + if ev.GetEventType().String() != "ptrace" { + return zeroValue + } + if ev.PTrace.Tracee == nil { + return zeroValue + } + if ev.PTrace.Tracee.Parent == nil { + return zeroValue + } + if !ev.PTrace.Tracee.HasParent() { + return []string{} + } + return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.PTrace.Tracee.Parent) +} + // GetPtraceTraceeParentCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentCapEffective() uint64 { zeroValue := uint64(0) @@ -11074,7 +11160,7 @@ func (ev *Event) GetPtraceTraceeParentEgroup() string { } // GetPtraceTraceeParentEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetPtraceTraceeParentEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetPtraceTraceeParentEnvp() []string { zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { return zeroValue @@ -11088,15 +11174,11 @@ func (ev *Event) GetPtraceTraceeParentEnvp(desiredKeys map[string]bool) []string if !ev.PTrace.Tracee.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessEnvp(ev, ev.PTrace.Tracee.Parent) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvp(ev, ev.PTrace.Tracee.Parent) } // GetPtraceTraceeParentEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetPtraceTraceeParentEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetPtraceTraceeParentEnvs() []string { zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { return zeroValue @@ -11110,11 +11192,7 @@ func (ev *Event) GetPtraceTraceeParentEnvs(desiredKeys map[string]bool) []string if !ev.PTrace.Tracee.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessEnvs(ev, ev.PTrace.Tracee.Parent) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvs(ev, ev.PTrace.Tracee.Parent) } // GetPtraceTraceeParentEnvsTruncated returns the value of the field, resolving if necessary @@ -11273,10 +11351,7 @@ func (ev *Event) GetPtraceTraceeParentFileHashes() []string { if !ev.PTrace.Tracee.Parent.IsNotKworker() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.PTrace.Tracee.Parent.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.PTrace.Tracee.Parent.FileEvent) } // GetPtraceTraceeParentFileInUpperLayer returns the value of the field, resolving if necessary @@ -11792,10 +11867,7 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileHashes() []string { if !ev.PTrace.Tracee.Parent.HasInterpreter() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.PTrace.Tracee.Parent.LinuxBinprm.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.PTrace.Tracee.Parent.LinuxBinprm.FileEvent) } // GetPtraceTraceeParentInterpreterFileInUpperLayer returns the value of the field, resolving if necessary @@ -12260,10 +12332,7 @@ func (ev *Event) GetPtraceTraceeParentUserSessionK8sGroups() []string { if !ev.PTrace.Tracee.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveK8SGroups(ev, &ev.PTrace.Tracee.Parent.UserSession) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.PTrace.Tracee.Parent.UserSession) } // GetPtraceTraceeParentUserSessionK8sUid returns the value of the field, resolving if necessary @@ -12383,10 +12452,7 @@ func (ev *Event) GetPtraceTraceeUserSessionK8sGroups() []string { if ev.PTrace.Tracee == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveK8SGroups(ev, &ev.PTrace.Tracee.Process.UserSession) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.PTrace.Tracee.Process.UserSession) } // GetPtraceTraceeUserSessionK8sUid returns the value of the field, resolving if necessary @@ -12473,10 +12539,7 @@ func (ev *Event) GetRemovexattrFileHashes() []string { if ev.GetEventType().String() != "removexattr" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.RemoveXAttr.File) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.RemoveXAttr.File) } // GetRemovexattrFileInUpperLayer returns the value of the field, resolving if necessary @@ -12674,10 +12737,7 @@ func (ev *Event) GetRenameFileDestinationHashes() []string { if ev.GetEventType().String() != "rename" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Rename.New) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Rename.New) } // GetRenameFileDestinationInUpperLayer returns the value of the field, resolving if necessary @@ -12848,10 +12908,7 @@ func (ev *Event) GetRenameFileHashes() []string { if ev.GetEventType().String() != "rename" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Rename.Old) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Rename.Old) } // GetRenameFileInUpperLayer returns the value of the field, resolving if necessary @@ -13040,10 +13097,7 @@ func (ev *Event) GetRmdirFileHashes() []string { if ev.GetEventType().String() != "rmdir" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Rmdir.File) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Rmdir.File) } // GetRmdirFileInUpperLayer returns the value of the field, resolving if necessary @@ -13394,10 +13448,7 @@ func (ev *Event) GetSetxattrFileHashes() []string { if ev.GetEventType().String() != "setxattr" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.SetXAttr.File) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.SetXAttr.File) } // GetSetxattrFileInUpperLayer returns the value of the field, resolving if necessary @@ -13637,6 +13688,31 @@ func (ev *Event) GetSignalTargetAncestorsArgsOptions() []string { return values } +// GetSignalTargetAncestorsArgsScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetSignalTargetAncestorsArgsScrubbed() []string { + zeroValue := []string{} + if ev.GetEventType().String() != "signal" { + return zeroValue + } + if ev.Signal.Target == nil { + return zeroValue + } + if ev.Signal.Target.Ancestor == nil { + return zeroValue + } + var values []string + ctx := eval.NewContext(ev) + iterator := &ProcessAncestorsIterator{} + ptr := iterator.Front(ctx) + for ptr != nil { + element := (*ProcessCacheEntry)(ptr) + result := ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, &element.ProcessContext.Process) + values = append(values, result) + ptr = iterator.Next() + } + return values +} + // GetSignalTargetAncestorsArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsArgsTruncated() []bool { zeroValue := []bool{} @@ -13680,7 +13756,7 @@ func (ev *Event) GetSignalTargetAncestorsArgv() []string { ptr := iterator.Front(ctx) for ptr != nil { element := (*ProcessCacheEntry)(ptr) - result := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &element.ProcessContext.Process) + result := ev.FieldHandlers.ResolveProcessArgv(ev, &element.ProcessContext.Process) values = append(values, result...) ptr = iterator.Next() } @@ -13712,6 +13788,31 @@ func (ev *Event) GetSignalTargetAncestorsArgv0() []string { return values } +// GetSignalTargetAncestorsArgvScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetSignalTargetAncestorsArgvScrubbed() []string { + zeroValue := []string{} + if ev.GetEventType().String() != "signal" { + return zeroValue + } + if ev.Signal.Target == nil { + return zeroValue + } + if ev.Signal.Target.Ancestor == nil { + return zeroValue + } + var values []string + ctx := eval.NewContext(ev) + iterator := &ProcessAncestorsIterator{} + ptr := iterator.Front(ctx) + for ptr != nil { + element := (*ProcessCacheEntry)(ptr) + result := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &element.ProcessContext.Process) + values = append(values, result...) + ptr = iterator.Next() + } + return values +} + // GetSignalTargetAncestorsCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsCapEffective() []uint64 { zeroValue := []uint64{} @@ -13888,7 +13989,7 @@ func (ev *Event) GetSignalTargetAncestorsEgroup() []string { } // GetSignalTargetAncestorsEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetSignalTargetAncestorsEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetSignalTargetAncestorsEnvp() []string { zeroValue := []string{} if ev.GetEventType().String() != "signal" { return zeroValue @@ -13906,7 +14007,6 @@ func (ev *Event) GetSignalTargetAncestorsEnvp(desiredKeys map[string]bool) []str for ptr != nil { element := (*ProcessCacheEntry)(ptr) result := ev.FieldHandlers.ResolveProcessEnvp(ev, &element.ProcessContext.Process) - result = filterEnvs(result, desiredKeys) values = append(values, result...) ptr = iterator.Next() } @@ -13914,7 +14014,7 @@ func (ev *Event) GetSignalTargetAncestorsEnvp(desiredKeys map[string]bool) []str } // GetSignalTargetAncestorsEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetSignalTargetAncestorsEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetSignalTargetAncestorsEnvs() []string { zeroValue := []string{} if ev.GetEventType().String() != "signal" { return zeroValue @@ -13932,7 +14032,6 @@ func (ev *Event) GetSignalTargetAncestorsEnvs(desiredKeys map[string]bool) []str for ptr != nil { element := (*ProcessCacheEntry)(ptr) result := ev.FieldHandlers.ResolveProcessEnvs(ev, &element.ProcessContext.Process) - result = filterEnvs(result, desiredKeys) values = append(values, result...) ptr = iterator.Next() } @@ -15460,10 +15559,7 @@ func (ev *Event) GetSignalTargetArgsFlags() []string { if ev.Signal.Target == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgsFlags(ev, &ev.Signal.Target.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsFlags(ev, &ev.Signal.Target.Process) } // GetSignalTargetArgsOptions returns the value of the field, resolving if necessary @@ -15475,10 +15571,19 @@ func (ev *Event) GetSignalTargetArgsOptions() []string { if ev.Signal.Target == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgsOptions(ev, &ev.Signal.Target.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsOptions(ev, &ev.Signal.Target.Process) +} + +// GetSignalTargetArgsScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetSignalTargetArgsScrubbed() string { + zeroValue := "" + if ev.GetEventType().String() != "signal" { + return zeroValue + } + if ev.Signal.Target == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, &ev.Signal.Target.Process) } // GetSignalTargetArgsTruncated returns the value of the field, resolving if necessary @@ -15502,10 +15607,7 @@ func (ev *Event) GetSignalTargetArgv() []string { if ev.Signal.Target == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &ev.Signal.Target.Process) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgv(ev, &ev.Signal.Target.Process) } // GetSignalTargetArgv0 returns the value of the field, resolving if necessary @@ -15520,6 +15622,18 @@ func (ev *Event) GetSignalTargetArgv0() string { return ev.FieldHandlers.ResolveProcessArgv0(ev, &ev.Signal.Target.Process) } +// GetSignalTargetArgvScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetSignalTargetArgvScrubbed() []string { + zeroValue := []string{} + if ev.GetEventType().String() != "signal" { + return zeroValue + } + if ev.Signal.Target == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &ev.Signal.Target.Process) +} + // GetSignalTargetCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetCapEffective() uint64 { zeroValue := uint64(0) @@ -15605,7 +15719,7 @@ func (ev *Event) GetSignalTargetEgroup() string { } // GetSignalTargetEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetSignalTargetEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetSignalTargetEnvp() []string { zeroValue := []string{} if ev.GetEventType().String() != "signal" { return zeroValue @@ -15613,15 +15727,11 @@ func (ev *Event) GetSignalTargetEnvp(desiredKeys map[string]bool) []string { if ev.Signal.Target == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.Signal.Target.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.Signal.Target.Process) } // GetSignalTargetEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetSignalTargetEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetSignalTargetEnvs() []string { zeroValue := []string{} if ev.GetEventType().String() != "signal" { return zeroValue @@ -15629,11 +15739,7 @@ func (ev *Event) GetSignalTargetEnvs(desiredKeys map[string]bool) []string { if ev.Signal.Target == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.Signal.Target.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.Signal.Target.Process) } // GetSignalTargetEnvsTruncated returns the value of the field, resolving if necessary @@ -15768,10 +15874,7 @@ func (ev *Event) GetSignalTargetFileHashes() []string { if !ev.Signal.Target.Process.IsNotKworker() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Signal.Target.Process.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Signal.Target.Process.FileEvent) } // GetSignalTargetFileInUpperLayer returns the value of the field, resolving if necessary @@ -16149,10 +16252,7 @@ func (ev *Event) GetSignalTargetInterpreterFileHashes() []string { if !ev.Signal.Target.Process.HasInterpreter() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Signal.Target.Process.LinuxBinprm.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Signal.Target.Process.LinuxBinprm.FileEvent) } // GetSignalTargetInterpreterFileInUpperLayer returns the value of the field, resolving if necessary @@ -16431,10 +16531,7 @@ func (ev *Event) GetSignalTargetParentArgsFlags() []string { if !ev.Signal.Target.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessArgsFlags(ev, ev.Signal.Target.Parent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsFlags(ev, ev.Signal.Target.Parent) } // GetSignalTargetParentArgsOptions returns the value of the field, resolving if necessary @@ -16452,10 +16549,25 @@ func (ev *Event) GetSignalTargetParentArgsOptions() []string { if !ev.Signal.Target.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessArgsOptions(ev, ev.Signal.Target.Parent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgsOptions(ev, ev.Signal.Target.Parent) +} + +// GetSignalTargetParentArgsScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetSignalTargetParentArgsScrubbed() string { + zeroValue := "" + if ev.GetEventType().String() != "signal" { + return zeroValue + } + if ev.Signal.Target == nil { + return zeroValue + } + if ev.Signal.Target.Parent == nil { + return zeroValue + } + if !ev.Signal.Target.HasParent() { + return "" + } + return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, ev.Signal.Target.Parent) } // GetSignalTargetParentArgsTruncated returns the value of the field, resolving if necessary @@ -16491,10 +16603,7 @@ func (ev *Event) GetSignalTargetParentArgv() []string { if !ev.Signal.Target.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.Signal.Target.Parent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessArgv(ev, ev.Signal.Target.Parent) } // GetSignalTargetParentArgv0 returns the value of the field, resolving if necessary @@ -16515,6 +16624,24 @@ func (ev *Event) GetSignalTargetParentArgv0() string { return ev.FieldHandlers.ResolveProcessArgv0(ev, ev.Signal.Target.Parent) } +// GetSignalTargetParentArgvScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetSignalTargetParentArgvScrubbed() []string { + zeroValue := []string{} + if ev.GetEventType().String() != "signal" { + return zeroValue + } + if ev.Signal.Target == nil { + return zeroValue + } + if ev.Signal.Target.Parent == nil { + return zeroValue + } + if !ev.Signal.Target.HasParent() { + return []string{} + } + return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.Signal.Target.Parent) +} + // GetSignalTargetParentCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentCapEffective() uint64 { zeroValue := uint64(0) @@ -16642,7 +16769,7 @@ func (ev *Event) GetSignalTargetParentEgroup() string { } // GetSignalTargetParentEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetSignalTargetParentEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetSignalTargetParentEnvp() []string { zeroValue := []string{} if ev.GetEventType().String() != "signal" { return zeroValue @@ -16656,15 +16783,11 @@ func (ev *Event) GetSignalTargetParentEnvp(desiredKeys map[string]bool) []string if !ev.Signal.Target.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Signal.Target.Parent) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Signal.Target.Parent) } // GetSignalTargetParentEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetSignalTargetParentEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetSignalTargetParentEnvs() []string { zeroValue := []string{} if ev.GetEventType().String() != "signal" { return zeroValue @@ -16678,11 +16801,7 @@ func (ev *Event) GetSignalTargetParentEnvs(desiredKeys map[string]bool) []string if !ev.Signal.Target.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Signal.Target.Parent) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Signal.Target.Parent) } // GetSignalTargetParentEnvsTruncated returns the value of the field, resolving if necessary @@ -16841,10 +16960,7 @@ func (ev *Event) GetSignalTargetParentFileHashes() []string { if !ev.Signal.Target.Parent.IsNotKworker() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Signal.Target.Parent.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Signal.Target.Parent.FileEvent) } // GetSignalTargetParentFileInUpperLayer returns the value of the field, resolving if necessary @@ -17360,10 +17476,7 @@ func (ev *Event) GetSignalTargetParentInterpreterFileHashes() []string { if !ev.Signal.Target.Parent.HasInterpreter() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Signal.Target.Parent.LinuxBinprm.FileEvent) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Signal.Target.Parent.LinuxBinprm.FileEvent) } // GetSignalTargetParentInterpreterFileInUpperLayer returns the value of the field, resolving if necessary @@ -17828,10 +17941,7 @@ func (ev *Event) GetSignalTargetParentUserSessionK8sGroups() []string { if !ev.Signal.Target.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveK8SGroups(ev, &ev.Signal.Target.Parent.UserSession) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.Signal.Target.Parent.UserSession) } // GetSignalTargetParentUserSessionK8sUid returns the value of the field, resolving if necessary @@ -17951,10 +18061,7 @@ func (ev *Event) GetSignalTargetUserSessionK8sGroups() []string { if ev.Signal.Target == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveK8SGroups(ev, &ev.Signal.Target.Process.UserSession) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.Signal.Target.Process.UserSession) } // GetSignalTargetUserSessionK8sUid returns the value of the field, resolving if necessary @@ -18032,10 +18139,7 @@ func (ev *Event) GetSpliceFileHashes() []string { if ev.GetEventType().String() != "splice" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Splice.File) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Splice.File) } // GetSpliceFileInUpperLayer returns the value of the field, resolving if necessary @@ -18202,7 +18306,7 @@ func (ev *Event) GetSpliceRetval() int64 { // GetTimestamp returns the value of the field, resolving if necessary func (ev *Event) GetTimestamp() time.Time { - return ev.FieldHandlers.ResolveEventTime(ev) + return ev.FieldHandlers.ResolveEventTime(ev, &ev.BaseEvent) } // GetUnlinkFileChangeTime returns the value of the field, resolving if necessary @@ -18247,10 +18351,7 @@ func (ev *Event) GetUnlinkFileHashes() []string { if ev.GetEventType().String() != "unlink" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Unlink.File) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Unlink.File) } // GetUnlinkFileInUpperLayer returns the value of the field, resolving if necessary @@ -18466,10 +18567,7 @@ func (ev *Event) GetUtimesFileHashes() []string { if ev.GetEventType().String() != "utimes" { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Utimes.File) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Utimes.File) } // GetUtimesFileInUpperLayer returns the value of the field, resolving if necessary diff --git a/pkg/security/secl/model/field_accessors_windows.go b/pkg/security/secl/model/field_accessors_windows.go index 244edd70aea1e..ec41b62a4ad1e 100644 --- a/pkg/security/secl/model/field_accessors_windows.go +++ b/pkg/security/secl/model/field_accessors_windows.go @@ -11,7 +11,6 @@ package model import ( "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" - "net" "time" ) @@ -39,10 +38,7 @@ func (ev *Event) GetContainerTags() []string { if ev.BaseEvent.ContainerContext == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveContainerTags(ev, ev.BaseEvent.ContainerContext) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveContainerTags(ev, ev.BaseEvent.ContainerContext) } // GetEventTimestamp returns the value of the field, resolving if necessary @@ -59,7 +55,19 @@ func (ev *Event) GetExecCmdline() string { if ev.Exec.Process == nil { return zeroValue } - return ev.Exec.Process.CmdLine + return ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.Exec.Process) +} + +// GetExecCmdlineScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetExecCmdlineScrubbed() string { + zeroValue := "" + if ev.GetEventType().String() != "exec" { + return zeroValue + } + if ev.Exec.Process == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessCmdLineScrubbed(ev, ev.Exec.Process) } // GetExecContainerId returns the value of the field, resolving if necessary @@ -87,7 +95,7 @@ func (ev *Event) GetExecCreatedAt() int { } // GetExecEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetExecEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetExecEnvp() []string { zeroValue := []string{} if ev.GetEventType().String() != "exec" { return zeroValue @@ -95,15 +103,11 @@ func (ev *Event) GetExecEnvp(desiredKeys map[string]bool) []string { if ev.Exec.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exec.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exec.Process) } // GetExecEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetExecEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetExecEnvs() []string { zeroValue := []string{} if ev.GetEventType().String() != "exec" { return zeroValue @@ -111,11 +115,7 @@ func (ev *Event) GetExecEnvs(desiredKeys map[string]bool) []string { if ev.Exec.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exec.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exec.Process) } // GetExecExecTime returns the value of the field, resolving if necessary @@ -232,7 +232,19 @@ func (ev *Event) GetExitCmdline() string { if ev.Exit.Process == nil { return zeroValue } - return ev.Exit.Process.CmdLine + return ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.Exit.Process) +} + +// GetExitCmdlineScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetExitCmdlineScrubbed() string { + zeroValue := "" + if ev.GetEventType().String() != "exit" { + return zeroValue + } + if ev.Exit.Process == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessCmdLineScrubbed(ev, ev.Exit.Process) } // GetExitCode returns the value of the field, resolving if necessary @@ -269,7 +281,7 @@ func (ev *Event) GetExitCreatedAt() int { } // GetExitEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetExitEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetExitEnvp() []string { zeroValue := []string{} if ev.GetEventType().String() != "exit" { return zeroValue @@ -277,15 +289,11 @@ func (ev *Event) GetExitEnvp(desiredKeys map[string]bool) []string { if ev.Exit.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exit.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exit.Process) } // GetExitEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetExitEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetExitEnvs() []string { zeroValue := []string{} if ev.GetEventType().String() != "exit" { return zeroValue @@ -293,11 +301,7 @@ func (ev *Event) GetExitEnvs(desiredKeys map[string]bool) []string { if ev.Exit.Process == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exit.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exit.Process) } // GetExitExecTime returns the value of the field, resolving if necessary @@ -396,71 +400,30 @@ func (ev *Event) GetExitPpid() uint32 { return ev.Exit.Process.PPid } -// GetNetworkDestinationIp returns the value of the field, resolving if necessary -func (ev *Event) GetNetworkDestinationIp() net.IPNet { - zeroValue := net.IPNet{} - if ev.GetEventType().String() != "dns" { - return zeroValue - } - return ev.BaseEvent.NetworkContext.Destination.IPNet -} - -// GetNetworkDestinationPort returns the value of the field, resolving if necessary -func (ev *Event) GetNetworkDestinationPort() uint16 { - zeroValue := uint16(0) - if ev.GetEventType().String() != "dns" { - return zeroValue - } - return ev.BaseEvent.NetworkContext.Destination.Port -} - -// GetNetworkL3Protocol returns the value of the field, resolving if necessary -func (ev *Event) GetNetworkL3Protocol() uint16 { - zeroValue := uint16(0) - if ev.GetEventType().String() != "dns" { - return zeroValue - } - return ev.BaseEvent.NetworkContext.L3Protocol -} - -// GetNetworkL4Protocol returns the value of the field, resolving if necessary -func (ev *Event) GetNetworkL4Protocol() uint16 { - zeroValue := uint16(0) - if ev.GetEventType().String() != "dns" { - return zeroValue - } - return ev.BaseEvent.NetworkContext.L4Protocol -} - -// GetNetworkSize returns the value of the field, resolving if necessary -func (ev *Event) GetNetworkSize() uint32 { - zeroValue := uint32(0) - if ev.GetEventType().String() != "dns" { +// GetProcessAncestorsCmdline returns the value of the field, resolving if necessary +func (ev *Event) GetProcessAncestorsCmdline() []string { + zeroValue := []string{} + if ev.BaseEvent.ProcessContext == nil { return zeroValue } - return ev.BaseEvent.NetworkContext.Size -} - -// GetNetworkSourceIp returns the value of the field, resolving if necessary -func (ev *Event) GetNetworkSourceIp() net.IPNet { - zeroValue := net.IPNet{} - if ev.GetEventType().String() != "dns" { + if ev.BaseEvent.ProcessContext.Ancestor == nil { return zeroValue } - return ev.BaseEvent.NetworkContext.Source.IPNet -} - -// GetNetworkSourcePort returns the value of the field, resolving if necessary -func (ev *Event) GetNetworkSourcePort() uint16 { - zeroValue := uint16(0) - if ev.GetEventType().String() != "dns" { - return zeroValue + var values []string + ctx := eval.NewContext(ev) + iterator := &ProcessAncestorsIterator{} + ptr := iterator.Front(ctx) + for ptr != nil { + element := (*ProcessCacheEntry)(ptr) + result := ev.FieldHandlers.ResolveProcessCmdLine(ev, &element.ProcessContext.Process) + values = append(values, result) + ptr = iterator.Next() } - return ev.BaseEvent.NetworkContext.Source.Port + return values } -// GetProcessAncestorsCmdline returns the value of the field, resolving if necessary -func (ev *Event) GetProcessAncestorsCmdline() []string { +// GetProcessAncestorsCmdlineScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetProcessAncestorsCmdlineScrubbed() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue @@ -474,7 +437,7 @@ func (ev *Event) GetProcessAncestorsCmdline() []string { ptr := iterator.Front(ctx) for ptr != nil { element := (*ProcessCacheEntry)(ptr) - result := element.ProcessContext.Process.CmdLine + result := ev.FieldHandlers.ResolveProcessCmdLineScrubbed(ev, &element.ProcessContext.Process) values = append(values, result) ptr = iterator.Next() } @@ -526,7 +489,7 @@ func (ev *Event) GetProcessAncestorsCreatedAt() []int { } // GetProcessAncestorsEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetProcessAncestorsEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetProcessAncestorsEnvp() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue @@ -541,7 +504,6 @@ func (ev *Event) GetProcessAncestorsEnvp(desiredKeys map[string]bool) []string { for ptr != nil { element := (*ProcessCacheEntry)(ptr) result := ev.FieldHandlers.ResolveProcessEnvp(ev, &element.ProcessContext.Process) - result = filterEnvs(result, desiredKeys) values = append(values, result...) ptr = iterator.Next() } @@ -549,7 +511,7 @@ func (ev *Event) GetProcessAncestorsEnvp(desiredKeys map[string]bool) []string { } // GetProcessAncestorsEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetProcessAncestorsEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetProcessAncestorsEnvs() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue @@ -564,7 +526,6 @@ func (ev *Event) GetProcessAncestorsEnvs(desiredKeys map[string]bool) []string { for ptr != nil { element := (*ProcessCacheEntry)(ptr) result := ev.FieldHandlers.ResolveProcessEnvs(ev, &element.ProcessContext.Process) - result = filterEnvs(result, desiredKeys) values = append(values, result...) ptr = iterator.Next() } @@ -709,7 +670,16 @@ func (ev *Event) GetProcessCmdline() string { if ev.BaseEvent.ProcessContext == nil { return zeroValue } - return ev.BaseEvent.ProcessContext.Process.CmdLine + return ev.FieldHandlers.ResolveProcessCmdLine(ev, &ev.BaseEvent.ProcessContext.Process) +} + +// GetProcessCmdlineScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetProcessCmdlineScrubbed() string { + zeroValue := "" + if ev.BaseEvent.ProcessContext == nil { + return zeroValue + } + return ev.FieldHandlers.ResolveProcessCmdLineScrubbed(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessContainerId returns the value of the field, resolving if necessary @@ -731,29 +701,21 @@ func (ev *Event) GetProcessCreatedAt() int { } // GetProcessEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetProcessEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetProcessEnvp() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.BaseEvent.ProcessContext.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetProcessEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetProcessEnvs() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue } - resolvedField := ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.BaseEvent.ProcessContext.Process) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessExecTime returns the value of the field, resolving if necessary @@ -822,7 +784,22 @@ func (ev *Event) GetProcessParentCmdline() string { if !ev.BaseEvent.ProcessContext.HasParent() { return "" } - return ev.BaseEvent.ProcessContext.Parent.CmdLine + return ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.BaseEvent.ProcessContext.Parent) +} + +// GetProcessParentCmdlineScrubbed returns the value of the field, resolving if necessary +func (ev *Event) GetProcessParentCmdlineScrubbed() string { + zeroValue := "" + if ev.BaseEvent.ProcessContext == nil { + return zeroValue + } + if ev.BaseEvent.ProcessContext.Parent == nil { + return zeroValue + } + if !ev.BaseEvent.ProcessContext.HasParent() { + return "" + } + return ev.FieldHandlers.ResolveProcessCmdLineScrubbed(ev, ev.BaseEvent.ProcessContext.Parent) } // GetProcessParentContainerId returns the value of the field, resolving if necessary @@ -856,7 +833,7 @@ func (ev *Event) GetProcessParentCreatedAt() int { } // GetProcessParentEnvp returns the value of the field, resolving if necessary -func (ev *Event) GetProcessParentEnvp(desiredKeys map[string]bool) []string { +func (ev *Event) GetProcessParentEnvp() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue @@ -867,15 +844,11 @@ func (ev *Event) GetProcessParentEnvp(desiredKeys map[string]bool) []string { if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessEnvp(ev, ev.BaseEvent.ProcessContext.Parent) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvp(ev, ev.BaseEvent.ProcessContext.Parent) } // GetProcessParentEnvs returns the value of the field, resolving if necessary -func (ev *Event) GetProcessParentEnvs(desiredKeys map[string]bool) []string { +func (ev *Event) GetProcessParentEnvs() []string { zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { return zeroValue @@ -886,11 +859,7 @@ func (ev *Event) GetProcessParentEnvs(desiredKeys map[string]bool) []string { if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} } - resolvedField := ev.FieldHandlers.ResolveProcessEnvs(ev, ev.BaseEvent.ProcessContext.Parent) - resolvedField = filterEnvs(resolvedField, desiredKeys) - fieldCopy := make([]string, len(resolvedField)) - copy(fieldCopy, resolvedField) - return fieldCopy + return ev.FieldHandlers.ResolveProcessEnvs(ev, ev.BaseEvent.ProcessContext.Parent) } // GetProcessParentFileName returns the value of the field, resolving if necessary @@ -997,5 +966,5 @@ func (ev *Event) GetProcessPpid() uint32 { // GetTimestamp returns the value of the field, resolving if necessary func (ev *Event) GetTimestamp() time.Time { - return ev.FieldHandlers.ResolveEventTime(ev) + return ev.FieldHandlers.ResolveEventTime(ev, &ev.BaseEvent) } diff --git a/pkg/security/secl/model/field_handlers_unix.go b/pkg/security/secl/model/field_handlers_unix.go index bd45e1cfba837..c55a34d3fa9ab 100644 --- a/pkg/security/secl/model/field_handlers_unix.go +++ b/pkg/security/secl/model/field_handlers_unix.go @@ -9,6 +9,10 @@ package model +import ( + "time" +) + // ResolveFields resolves all the fields associate to the event type. Context fields are automatically resolved. func (ev *Event) ResolveFields() { ev.resolveFields(false) @@ -234,7 +238,6 @@ func (ev *Event) resolveFields(forADs bool) { _ = ev.FieldHandlers.ResolveChownGID(ev, &ev.Chown) case "dns": _ = ev.FieldHandlers.ResolveNetworkDeviceIfName(ev, &ev.NetworkContext.Device) - _ = ev.FieldHandlers.ResolveNetworkDeviceIfName(ev, &ev.NetworkContext.Device) case "exec": if ev.Exec.Process.IsNotKworker() { _ = ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Exec.Process.FileEvent.FileFields) @@ -311,6 +314,8 @@ func (ev *Event) resolveFields(forADs bool) { _ = ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exec.Process) _ = ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exec.Process) _ = ev.FieldHandlers.ResolveProcessEnvsTruncated(ev, ev.Exec.Process) + _ = ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, ev.Exec.Process) + _ = ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.Exec.Process) case "exit": if ev.Exit.Process.IsNotKworker() { _ = ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Exit.Process.FileEvent.FileFields) @@ -387,6 +392,8 @@ func (ev *Event) resolveFields(forADs bool) { _ = ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exit.Process) _ = ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exit.Process) _ = ev.FieldHandlers.ResolveProcessEnvsTruncated(ev, ev.Exit.Process) + _ = ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, ev.Exit.Process) + _ = ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.Exit.Process) case "link": _ = ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Link.Source.FileFields) _ = ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Link.Source.FileFields) @@ -456,6 +463,7 @@ func (ev *Event) resolveFields(forADs bool) { case "mount": _ = ev.FieldHandlers.ResolveMountPointPath(ev, &ev.Mount) _ = ev.FieldHandlers.ResolveMountSourcePath(ev, &ev.Mount) + _ = ev.FieldHandlers.ResolveMountRootPath(ev, &ev.Mount) case "mprotect": case "open": _ = ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Open.File.FileFields) @@ -546,6 +554,8 @@ func (ev *Event) resolveFields(forADs bool) { _ = ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.PTrace.Tracee.Process) _ = ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.PTrace.Tracee.Process) _ = ev.FieldHandlers.ResolveProcessEnvsTruncated(ev, &ev.PTrace.Tracee.Process) + _ = ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, &ev.PTrace.Tracee.Process) + _ = ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &ev.PTrace.Tracee.Process) if ev.PTrace.Tracee.HasParent() && ev.PTrace.Tracee.Parent.IsNotKworker() { _ = ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.PTrace.Tracee.Parent.FileEvent.FileFields) } @@ -643,6 +653,12 @@ func (ev *Event) resolveFields(forADs bool) { if ev.PTrace.Tracee.HasParent() { _ = ev.FieldHandlers.ResolveProcessEnvsTruncated(ev, ev.PTrace.Tracee.Parent) } + if ev.PTrace.Tracee.HasParent() { + _ = ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, ev.PTrace.Tracee.Parent) + } + if ev.PTrace.Tracee.HasParent() { + _ = ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.PTrace.Tracee.Parent) + } case "removexattr": _ = ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.RemoveXAttr.File.FileFields) _ = ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.RemoveXAttr.File.FileFields) @@ -797,6 +813,8 @@ func (ev *Event) resolveFields(forADs bool) { _ = ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.Signal.Target.Process) _ = ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.Signal.Target.Process) _ = ev.FieldHandlers.ResolveProcessEnvsTruncated(ev, &ev.Signal.Target.Process) + _ = ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, &ev.Signal.Target.Process) + _ = ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &ev.Signal.Target.Process) if ev.Signal.Target.HasParent() && ev.Signal.Target.Parent.IsNotKworker() { _ = ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Signal.Target.Parent.FileEvent.FileFields) } @@ -894,6 +912,12 @@ func (ev *Event) resolveFields(forADs bool) { if ev.Signal.Target.HasParent() { _ = ev.FieldHandlers.ResolveProcessEnvsTruncated(ev, ev.Signal.Target.Parent) } + if ev.Signal.Target.HasParent() { + _ = ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, ev.Signal.Target.Parent) + } + if ev.Signal.Target.HasParent() { + _ = ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.Signal.Target.Parent) + } case "splice": _ = ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Splice.File.FileFields) _ = ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Splice.File.FileFields) @@ -944,6 +968,7 @@ type FieldHandlers interface { ResolveContainerCreatedAt(ev *Event, e *ContainerContext) int ResolveContainerID(ev *Event, e *ContainerContext) string ResolveContainerTags(ev *Event, e *ContainerContext) []string + ResolveEventTime(ev *Event, e *BaseEvent) time.Time ResolveEventTimestamp(ev *Event, e *BaseEvent) int ResolveFileBasename(ev *Event, e *FileEvent) string ResolveFileFieldsGroup(ev *Event, e *FileFields) string @@ -958,6 +983,7 @@ type FieldHandlers interface { ResolveModuleArgs(ev *Event, e *LoadModuleEvent) string ResolveModuleArgv(ev *Event, e *LoadModuleEvent) []string ResolveMountPointPath(ev *Event, e *MountEvent) string + ResolveMountRootPath(ev *Event, e *MountEvent) string ResolveMountSourcePath(ev *Event, e *MountEvent) string ResolveNetworkDeviceIfName(ev *Event, e *NetworkDeviceContext) string ResolvePackageName(ev *Event, e *FileEvent) string @@ -966,10 +992,11 @@ type FieldHandlers interface { ResolveProcessArgs(ev *Event, e *Process) string ResolveProcessArgsFlags(ev *Event, e *Process) []string ResolveProcessArgsOptions(ev *Event, e *Process) []string + ResolveProcessArgsScrubbed(ev *Event, e *Process) string ResolveProcessArgsTruncated(ev *Event, e *Process) bool ResolveProcessArgv(ev *Event, e *Process) []string - ResolveProcessArgvScrubbed(ev *Event, e *Process) []string ResolveProcessArgv0(ev *Event, e *Process) string + ResolveProcessArgvScrubbed(ev *Event, e *Process) []string ResolveProcessCreatedAt(ev *Event, e *Process) int ResolveProcessEnvp(ev *Event, e *Process) []string ResolveProcessEnvs(ev *Event, e *Process) []string @@ -1001,6 +1028,9 @@ func (dfh *DefaultFieldHandlers) ResolveContainerID(ev *Event, e *ContainerConte func (dfh *DefaultFieldHandlers) ResolveContainerTags(ev *Event, e *ContainerContext) []string { return e.Tags } +func (dfh *DefaultFieldHandlers) ResolveEventTime(ev *Event, e *BaseEvent) time.Time { + return e.Timestamp +} func (dfh *DefaultFieldHandlers) ResolveEventTimestamp(ev *Event, e *BaseEvent) int { return int(e.TimestampRaw) } @@ -1043,6 +1073,9 @@ func (dfh *DefaultFieldHandlers) ResolveModuleArgv(ev *Event, e *LoadModuleEvent func (dfh *DefaultFieldHandlers) ResolveMountPointPath(ev *Event, e *MountEvent) string { return e.MountPointPath } +func (dfh *DefaultFieldHandlers) ResolveMountRootPath(ev *Event, e *MountEvent) string { + return e.MountRootPath +} func (dfh *DefaultFieldHandlers) ResolveMountSourcePath(ev *Event, e *MountEvent) string { return e.MountSourcePath } @@ -1063,14 +1096,17 @@ func (dfh *DefaultFieldHandlers) ResolveProcessArgsFlags(ev *Event, e *Process) func (dfh *DefaultFieldHandlers) ResolveProcessArgsOptions(ev *Event, e *Process) []string { return e.Argv } +func (dfh *DefaultFieldHandlers) ResolveProcessArgsScrubbed(ev *Event, e *Process) string { + return e.ArgsScrubbed +} func (dfh *DefaultFieldHandlers) ResolveProcessArgsTruncated(ev *Event, e *Process) bool { return e.ArgsTruncated } func (dfh *DefaultFieldHandlers) ResolveProcessArgv(ev *Event, e *Process) []string { return e.Argv } +func (dfh *DefaultFieldHandlers) ResolveProcessArgv0(ev *Event, e *Process) string { return e.Argv0 } func (dfh *DefaultFieldHandlers) ResolveProcessArgvScrubbed(ev *Event, e *Process) []string { - return e.Argv + return e.ArgvScrubbed } -func (dfh *DefaultFieldHandlers) ResolveProcessArgv0(ev *Event, e *Process) string { return e.Argv0 } func (dfh *DefaultFieldHandlers) ResolveProcessCreatedAt(ev *Event, e *Process) int { return int(e.CreatedAt) } diff --git a/pkg/security/secl/model/field_handlers_windows.go b/pkg/security/secl/model/field_handlers_windows.go index abfb2624666ac..38d63c5680825 100644 --- a/pkg/security/secl/model/field_handlers_windows.go +++ b/pkg/security/secl/model/field_handlers_windows.go @@ -9,6 +9,10 @@ package model +import ( + "time" +) + // ResolveFields resolves all the fields associate to the event type. Context fields are automatically resolved. func (ev *Event) ResolveFields() { ev.resolveFields(false) @@ -26,11 +30,15 @@ func (ev *Event) resolveFields(forADs bool) { _ = ev.FieldHandlers.ResolveContainerTags(ev, ev.BaseEvent.ContainerContext) } _ = ev.FieldHandlers.ResolveEventTimestamp(ev, &ev.BaseEvent) + _ = ev.FieldHandlers.ResolveProcessCmdLine(ev, &ev.BaseEvent.ProcessContext.Process) _ = ev.FieldHandlers.ResolveProcessCreatedAt(ev, &ev.BaseEvent.ProcessContext.Process) _ = ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.BaseEvent.ProcessContext.Process) _ = ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.BaseEvent.ProcessContext.Process) _ = ev.FieldHandlers.ResolveFileBasename(ev, &ev.BaseEvent.ProcessContext.Process.FileEvent) _ = ev.FieldHandlers.ResolveFilePath(ev, &ev.BaseEvent.ProcessContext.Process.FileEvent) + if ev.BaseEvent.ProcessContext.HasParent() { + _ = ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.BaseEvent.ProcessContext.Parent) + } if ev.BaseEvent.ProcessContext.HasParent() { _ = ev.FieldHandlers.ResolveProcessCreatedAt(ev, ev.BaseEvent.ProcessContext.Parent) } @@ -48,17 +56,20 @@ func (ev *Event) resolveFields(forADs bool) { } // resolve event specific fields switch ev.GetEventType().String() { - case "dns": case "exec": _ = ev.FieldHandlers.ResolveFilePath(ev, &ev.Exec.Process.FileEvent) _ = ev.FieldHandlers.ResolveFileBasename(ev, &ev.Exec.Process.FileEvent) _ = ev.FieldHandlers.ResolveProcessCreatedAt(ev, ev.Exec.Process) + _ = ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.Exec.Process) + _ = ev.FieldHandlers.ResolveProcessCmdLineScrubbed(ev, ev.Exec.Process) _ = ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exec.Process) _ = ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exec.Process) case "exit": _ = ev.FieldHandlers.ResolveFilePath(ev, &ev.Exit.Process.FileEvent) _ = ev.FieldHandlers.ResolveFileBasename(ev, &ev.Exit.Process.FileEvent) _ = ev.FieldHandlers.ResolveProcessCreatedAt(ev, ev.Exit.Process) + _ = ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.Exit.Process) + _ = ev.FieldHandlers.ResolveProcessCmdLineScrubbed(ev, ev.Exit.Process) _ = ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exit.Process) _ = ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exit.Process) } @@ -68,9 +79,12 @@ type FieldHandlers interface { ResolveContainerCreatedAt(ev *Event, e *ContainerContext) int ResolveContainerID(ev *Event, e *ContainerContext) string ResolveContainerTags(ev *Event, e *ContainerContext) []string + ResolveEventTime(ev *Event, e *BaseEvent) time.Time ResolveEventTimestamp(ev *Event, e *BaseEvent) int ResolveFileBasename(ev *Event, e *FileEvent) string ResolveFilePath(ev *Event, e *FileEvent) string + ResolveProcessCmdLine(ev *Event, e *Process) string + ResolveProcessCmdLineScrubbed(ev *Event, e *Process) string ResolveProcessCreatedAt(ev *Event, e *Process) int ResolveProcessEnvp(ev *Event, e *Process) []string ResolveProcessEnvs(ev *Event, e *Process) []string @@ -88,6 +102,9 @@ func (dfh *DefaultFieldHandlers) ResolveContainerID(ev *Event, e *ContainerConte func (dfh *DefaultFieldHandlers) ResolveContainerTags(ev *Event, e *ContainerContext) []string { return e.Tags } +func (dfh *DefaultFieldHandlers) ResolveEventTime(ev *Event, e *BaseEvent) time.Time { + return e.Timestamp +} func (dfh *DefaultFieldHandlers) ResolveEventTimestamp(ev *Event, e *BaseEvent) int { return int(e.TimestampRaw) } @@ -97,6 +114,12 @@ func (dfh *DefaultFieldHandlers) ResolveFileBasename(ev *Event, e *FileEvent) st func (dfh *DefaultFieldHandlers) ResolveFilePath(ev *Event, e *FileEvent) string { return e.PathnameStr } +func (dfh *DefaultFieldHandlers) ResolveProcessCmdLine(ev *Event, e *Process) string { + return e.CmdLine +} +func (dfh *DefaultFieldHandlers) ResolveProcessCmdLineScrubbed(ev *Event, e *Process) string { + return e.CmdLineScrubbed +} func (dfh *DefaultFieldHandlers) ResolveProcessCreatedAt(ev *Event, e *Process) int { return int(e.CreatedAt) } diff --git a/pkg/security/secl/model/model.go b/pkg/security/secl/model/model.go index b583e06c10899..e98a0ad19c05f 100644 --- a/pkg/security/secl/model/model.go +++ b/pkg/security/secl/model/model.go @@ -171,10 +171,8 @@ type BaseEvent struct { Rules []*MatchedRule `field:"-"` // context shared with all events - SpanContext SpanContext `field:"-" json:"-"` ProcessContext *ProcessContext `field:"process" event:"*"` ContainerContext *ContainerContext `field:"container" event:"*"` - NetworkContext NetworkContext `field:"network" event:"dns"` SecurityProfileContext SecurityProfileContext `field:"-"` // internal usage @@ -287,11 +285,6 @@ func (e *Event) RemoveFromFlags(flag uint32) { e.Flags ^= flag } -// HasProfile returns true if we found a profile for that event -func (e *Event) HasProfile() bool { - return e.SecurityProfileContext.Name != "" -} - // GetType returns the event type func (e *Event) GetType() string { return EventType(e.Type).String() @@ -340,7 +333,7 @@ func (e *Event) ResolveProcessCacheEntry() (*ProcessCacheEntry, bool) { // ResolveEventTime uses the field handler func (e *Event) ResolveEventTime() time.Time { - return e.FieldHandlers.ResolveEventTime(e) + return e.FieldHandlers.ResolveEventTime(e, &e.BaseEvent) } // GetProcessService uses the field handler @@ -591,9 +584,7 @@ type DNSEvent struct { type BaseExtraFieldHandlers interface { ResolveProcessCacheEntry(ev *Event) (*ProcessCacheEntry, bool) ResolveContainerContext(ev *Event) (*ContainerContext, bool) - ResolveEventTime(ev *Event) time.Time GetProcessService(ev *Event) string - ResolveK8SExtra(ev *Event, ctx *UserSessionContext) map[string][]string } // ResolveProcessCacheEntry stub implementation @@ -606,22 +597,7 @@ func (dfh *DefaultFieldHandlers) ResolveContainerContext(ev *Event) (*ContainerC return nil, false } -// ResolveEventTime stub implementation -func (dfh *DefaultFieldHandlers) ResolveEventTime(ev *Event) time.Time { - return ev.Timestamp -} - // GetProcessService stub implementation func (dfh *DefaultFieldHandlers) GetProcessService(ev *Event) string { return "" } - -// ResolveHashes resolves the hash of the provided file -func (dfh *DefaultFieldHandlers) ResolveHashes(eventType EventType, process *Process, file *FileEvent) []string { - return nil -} - -// ResolveK8SExtra resolves the K8S user session extra field -func (dfh *DefaultFieldHandlers) ResolveK8SExtra(_ *Event, _ *UserSessionContext) map[string][]string { - return nil -} diff --git a/pkg/security/secl/model/model_unix.go b/pkg/security/secl/model/model_unix.go index aca678af8b5fc..20fc528dd9eba 100644 --- a/pkg/security/secl/model/model_unix.go +++ b/pkg/security/secl/model/model_unix.go @@ -143,9 +143,8 @@ type Event struct { Async bool `field:"event.async,handler:ResolveAsync" event:"*"` // SECLDoc[event.async] Definition:`True if the syscall was asynchronous` // context - SpanContext SpanContext `field:"-" json:"-"` - NetworkContext NetworkContext `field:"network" event:"dns"` - SecurityProfileContext SecurityProfileContext `field:"-"` + SpanContext SpanContext `field:"-" json:"-"` + NetworkContext NetworkContext `field:"network" event:"dns"` // fim events Chmod ChmodEvent `field:"chmod" event:"chmod"` // [7.27] [File] A file’s permissions were changed @@ -284,6 +283,35 @@ func (p *Process) IsNotKworker() bool { return !p.IsKworker } +// GetProcessArgv returns the unscrubbed args of the event as an array. Use with caution. +func (p *Process) GetProcessArgv() ([]string, bool) { + if p.ArgsEntry == nil { + return p.Argv, p.ArgsTruncated + } + + argv := p.ArgsEntry.Values + if len(argv) > 0 { + argv = argv[1:] + } + p.Argv = argv + p.ArgsTruncated = p.ArgsTruncated || p.ArgsEntry.Truncated + return p.Argv, p.ArgsTruncated +} + +// GetProcessArgv0 returns the first arg of the event and whether the process arguments are truncated +func (p *Process) GetProcessArgv0() (string, bool) { + if p.ArgsEntry == nil { + return p.Argv0, p.ArgsTruncated + } + + argv := p.ArgsEntry.Values + if len(argv) > 0 { + p.Argv0 = argv[0] + } + p.ArgsTruncated = p.ArgsTruncated || p.ArgsEntry.Truncated + return p.Argv0, p.ArgsTruncated +} + // LinuxBinprm contains content from the linux_binprm struct, which holds the arguments used for loading binaries type LinuxBinprm struct { FileEvent FileEvent `field:"file"` @@ -328,13 +356,16 @@ type Process struct { // defined to generate accessors, ArgsTruncated and EnvsTruncated are used during by unmarshaller Argv0 string `field:"argv0,handler:ResolveProcessArgv0,weight:100"` // SECLDoc[argv0] Definition:`First argument of the process` - Args string `field:"args,handler:ResolveProcessArgs,weight:100"` // SECLDoc[args] Definition:`Arguments of the process (as a string, excluding argv0)` Example:`exec.args == "-sV -p 22,53,110,143,4564 198.116.0-255.1-127"` Description:`Matches any process with these exact arguments.` Example:`exec.args =~ "* -F * http*"` Description:`Matches any process that has the "-F" argument anywhere before an argument starting with "http".` - Argv []string `field:"argv,handler:ResolveProcessArgv,weight:100; args_flags,handler:ResolveProcessArgsFlags,opts:helper; args_options,handler:ResolveProcessArgsOptions,opts:helper"` // SECLDoc[argv] Definition:`Arguments of the process (as an array, excluding argv0)` Example:`exec.argv in ["127.0.0.1"]` Description:`Matches any process that has this IP address as one of its arguments.` SECLDoc[args_flags] Definition:`Flags in the process arguments` Example:`exec.args_flags in ["s"] && exec.args_flags in ["V"]` Description:`Matches any process with both "-s" and "-V" flags in its arguments. Also matches "-sV".` SECLDoc[args_options] Definition:`Argument of the process as options` Example:`exec.args_options in ["p=0-1024"]` Description:`Matches any process that has either "-p 0-1024" or "--p=0-1024" in its arguments.` + Args string `field:"args,handler:ResolveProcessArgs,weight:500"` // SECLDoc[args] Definition:`Arguments of the process (as a string, excluding argv0)` Example:`exec.args == "-sV -p 22,53,110,143,4564 198.116.0-255.1-127"` Description:`Matches any process with these exact arguments.` Example:`exec.args =~ "* -F * http*"` Description:`Matches any process that has the "-F" argument anywhere before an argument starting with "http".` + Argv []string `field:"argv,handler:ResolveProcessArgv,weight:500; args_flags,handler:ResolveProcessArgsFlags,opts:helper; args_options,handler:ResolveProcessArgsOptions,opts:helper"` // SECLDoc[argv] Definition:`Arguments of the process (as an array, excluding argv0)` Example:`exec.argv in ["127.0.0.1"]` Description:`Matches any process that has this IP address as one of its arguments.` SECLDoc[args_flags] Definition:`Flags in the process arguments` Example:`exec.args_flags in ["s"] && exec.args_flags in ["V"]` Description:`Matches any process with both "-s" and "-V" flags in its arguments. Also matches "-sV".` SECLDoc[args_options] Definition:`Argument of the process as options` Example:`exec.args_options in ["p=0-1024"]` Description:`Matches any process that has either "-p 0-1024" or "--p=0-1024" in its arguments.` ArgsTruncated bool `field:"args_truncated,handler:ResolveProcessArgsTruncated"` // SECLDoc[args_truncated] Definition:`Indicator of arguments truncation` Envs []string `field:"envs,handler:ResolveProcessEnvs,weight:100"` // SECLDoc[envs] Definition:`Environment variable names of the process` Envp []string `field:"envp,handler:ResolveProcessEnvp,weight:100"` // SECLDoc[envp] Definition:`Environment variables of the process` EnvsTruncated bool `field:"envs_truncated,handler:ResolveProcessEnvsTruncated"` // SECLDoc[envs_truncated] Definition:`Indicator of environment variables truncation` + ArgsScrubbed string `field:"args_scrubbed,handler:ResolveProcessArgsScrubbed,weight:500,opts:getters_only"` + ArgvScrubbed []string `field:"argv_scrubbed,handler:ResolveProcessArgvScrubbed,weight:500,opts:getters_only"` + // symlink to the process binary SymlinkPathnameStr [MaxSymlinks]string `field:"-" json:"-"` SymlinkBasenameStr string `field:"-" json:"-"` @@ -507,8 +538,10 @@ type MountEvent struct { Mount MountPointPath string `field:"mountpoint.path,handler:ResolveMountPointPath"` // SECLDoc[mountpoint.path] Definition:`Path of the mount point` MountSourcePath string `field:"source.path,handler:ResolveMountSourcePath"` // SECLDoc[source.path] Definition:`Source path of a bind mount` + MountRootPath string `field:"root.path,handler:ResolveMountRootPath"` // SECLDoc[root.path] Definition:`Root path of the mount` MountPointPathResolutionError error `field:"-"` MountSourcePathResolutionError error `field:"-"` + MountRootPathResolutionError error `field:"-"` } // UnshareMountNSEvent represents a mount cloned from a newly created mount namespace @@ -885,4 +918,15 @@ func (pl *PathLeaf) MarshalBinary() ([]byte, error) { type ExtraFieldHandlers interface { BaseExtraFieldHandlers ResolveHashes(eventType EventType, process *Process, file *FileEvent) []string + ResolveK8SExtra(ev *Event, ctx *UserSessionContext) map[string][]string +} + +// ResolveHashes resolves the hash of the provided file +func (dfh *DefaultFieldHandlers) ResolveHashes(_ EventType, _ *Process, _ *FileEvent) []string { + return nil +} + +// ResolveK8SExtra resolves the K8S user session extra field +func (dfh *DefaultFieldHandlers) ResolveK8SExtra(_ *Event, _ *UserSessionContext) map[string][]string { + return nil } diff --git a/pkg/security/secl/model/model_windows.go b/pkg/security/secl/model/model_windows.go index def1417c3f396..cad29d3d1d748 100644 --- a/pkg/security/secl/model/model_windows.go +++ b/pkg/security/secl/model/model_windows.go @@ -57,9 +57,11 @@ type Process struct { ArgsEntry *ArgsEntry `field:"-" json:"-"` EnvsEntry *EnvsEntry `field:"-" json:"-"` - CmdLine string `field:"cmdline"` // SECLDoc[cmdline] Definition:`Command line of the process (as a string, excluding argv0)` Example:`exec.args == "-sV -p 22,53,110,143,4564 198.116.0-255.1-127"` Description:`Matches any process with these exact arguments.` Example:`exec.args =~ "* -F * http*"` Description:`Matches any process that has the "-F" argument anywhere before an argument starting with "http".` - Envs []string `field:"envs,handler:ResolveProcessEnvs,weight:100"` // SECLDoc[envs] Definition:`Environment variable names of the process` - Envp []string `field:"envp,handler:ResolveProcessEnvp,weight:100"` // SECLDoc[envp] Definition:`Environment variables of the process` // SECLDoc[envp] Definition:`Environment variables of the process` + CmdLine string `field:"cmdline,handler:ResolveProcessCmdLine,weight:200"` // SECLDoc[cmdline] Definition:`Command line of the process` Example:`exec.cmdline == "-sV -p 22,53,110,143,4564 198.116.0-255.1-127"` Description:`Matches any process with these exact arguments.` Example:`exec.cmdline =~ "* -F * http*"` Description:`Matches any process that has the "-F" argument anywhere before an argument starting with "http".` + CmdLineScrubbed string `field:"cmdline_scrubbed,handler:ResolveProcessCmdLineScrubbed,weight:500,opts:getters_only"` + + Envs []string `field:"envs,handler:ResolveProcessEnvs,weight:100"` // SECLDoc[envs] Definition:`Environment variable names of the process` + Envp []string `field:"envp,handler:ResolveProcessEnvp,weight:100"` // SECLDoc[envp] Definition:`Environment variables of the process` // SECLDoc[envp] Definition:`Environment variables of the process` // cache version Variables eval.Variables `field:"-" json:"-"` diff --git a/pkg/security/security_profile/activity_tree/activity_tree.go b/pkg/security/security_profile/activity_tree/activity_tree.go index e7c89c76eb1ad..86577985a5bc2 100644 --- a/pkg/security/security_profile/activity_tree/activity_tree.go +++ b/pkg/security/security_profile/activity_tree/activity_tree.go @@ -224,7 +224,7 @@ func (at *ActivityTree) Debug(w io.Writer) { } // ScrubProcessArgsEnvs scrubs and retains process args and envs -func (at *ActivityTree) ScrubProcessArgsEnvs(resolver *process.Resolver) { +func (at *ActivityTree) ScrubProcessArgsEnvs(resolver *process.EBPFResolver) { // iterate through all the process nodes openList := make([]*ProcessNode, len(at.ProcessNodes)) copy(openList, at.ProcessNodes) @@ -266,7 +266,7 @@ func (at *ActivityTree) isEventValid(event *model.Event, dryRun bool) (bool, err } // Insert inserts the event in the activity tree -func (at *ActivityTree) Insert(event *model.Event, insertMissingProcesses bool, generationType NodeGenerationType, resolvers *resolvers.Resolvers) (bool, error) { +func (at *ActivityTree) Insert(event *model.Event, insertMissingProcesses bool, generationType NodeGenerationType, resolvers *resolvers.EBPFResolvers) (bool, error) { newEntry, err := at.insertEvent(event, false /* !dryRun */, insertMissingProcesses, generationType, resolvers) if newEntry { // this doesn't count the exec events which are counted separately @@ -276,13 +276,13 @@ func (at *ActivityTree) Insert(event *model.Event, insertMissingProcesses bool, } // Contains looks up the event in the activity tree -func (at *ActivityTree) Contains(event *model.Event, insertMissingProcesses bool, generationType NodeGenerationType, resolvers *resolvers.Resolvers) (bool, error) { +func (at *ActivityTree) Contains(event *model.Event, insertMissingProcesses bool, generationType NodeGenerationType, resolvers *resolvers.EBPFResolvers) (bool, error) { newEntry, err := at.insertEvent(event, true /* dryRun */, insertMissingProcesses, generationType, resolvers) return !newEntry, err } // insert inserts the event in the activity tree, returns true if the event generated a new entry in the tree -func (at *ActivityTree) insertEvent(event *model.Event, dryRun bool, insertMissingProcesses bool, generationType NodeGenerationType, resolvers *resolvers.Resolvers) (bool, error) { +func (at *ActivityTree) insertEvent(event *model.Event, dryRun bool, insertMissingProcesses bool, generationType NodeGenerationType, resolvers *resolvers.EBPFResolvers) (bool, error) { // sanity check if generationType == Unknown || generationType > MaxNodeGenerationType { return false, fmt.Errorf("invalid generation type: %v", generationType) @@ -448,7 +448,7 @@ func (at *ActivityTree) buildBranchAndLookupCookies(entry *model.ProcessCacheEnt } // CreateProcessNode looks up or inserts the provided entry in the tree -func (at *ActivityTree) CreateProcessNode(entry *model.ProcessCacheEntry, generationType NodeGenerationType, dryRun bool, resolvers *resolvers.Resolvers) (*ProcessNode, bool, error) { +func (at *ActivityTree) CreateProcessNode(entry *model.ProcessCacheEntry, generationType NodeGenerationType, dryRun bool, resolvers *resolvers.EBPFResolvers) (*ProcessNode, bool, error) { if entry == nil { return nil, false, nil } @@ -486,7 +486,7 @@ func (at *ActivityTree) CreateProcessNode(entry *model.ProcessCacheEntry, genera return at.insertBranch(parent, branchToInsert, generationType, dryRun, resolvers) } -func (at *ActivityTree) insertBranch(parent ProcessNodeParent, branchToInsert []*model.ProcessCacheEntry, generationType NodeGenerationType, dryRun bool, r *resolvers.Resolvers) (*ProcessNode, bool, error) { +func (at *ActivityTree) insertBranch(parent ProcessNodeParent, branchToInsert []*model.ProcessCacheEntry, generationType NodeGenerationType, dryRun bool, r *resolvers.EBPFResolvers) (*ProcessNode, bool, error) { var matchingNode *ProcessNode var branchIncrement int var newNode, newNodeFromRebase bool @@ -536,7 +536,7 @@ func (at *ActivityTree) insertBranch(parent ProcessNodeParent, branchToInsert [] // findBranch2 looks for the provided branch in the list of children. Returns the node that matches the // first node of the branch and true if a new entry was inserted. -func (at *ActivityTree) findBranch(parent ProcessNodeParent, branch []*model.ProcessCacheEntry, dryRun bool, generationType NodeGenerationType, resolvers *resolvers.Resolvers) (*ProcessNode, int, bool) { +func (at *ActivityTree) findBranch(parent ProcessNodeParent, branch []*model.ProcessCacheEntry, dryRun bool, generationType NodeGenerationType, resolvers *resolvers.EBPFResolvers) (*ProcessNode, int, bool) { for i := len(branch) - 1; i >= 0; i-- { branchCursor := branch[i] @@ -612,7 +612,7 @@ func (at *ActivityTree) findBranch(parent ProcessNodeParent, branch []*model.Pro // rebaseTree rebases the node identified by "nodeIndexToRebase" in the input "tree" onto a newly created branch made of // "branchToInsert" and appended to "treeToRebaseOnto". New nodes will be tagged with the input "generationType". // This function returns the top level node, owner of the newly inserted branch that lead to the rebased node -func (at *ActivityTree) rebaseTree(parent ProcessNodeParent, childIndexToRebase int, newParent ProcessNodeParent, branchToInsert []*model.ProcessCacheEntry, generationType NodeGenerationType, resolvers *resolvers.Resolvers) *ProcessNode { +func (at *ActivityTree) rebaseTree(parent ProcessNodeParent, childIndexToRebase int, newParent ProcessNodeParent, branchToInsert []*model.ProcessCacheEntry, generationType NodeGenerationType, resolvers *resolvers.EBPFResolvers) *ProcessNode { if len(branchToInsert) > 1 { // We know that the entries in "branch" are all "isExecChild = true" nodes, except the top level entry that might be // a "isExecChild = false" node. Similarly, all the nodes below parent.GetChildren()[childIndexToRebase] must be non diff --git a/pkg/security/security_profile/activity_tree/activity_tree_graph.go b/pkg/security/security_profile/activity_tree/activity_tree_graph.go index e3c26d7b47003..c2be7b3bc5709 100644 --- a/pkg/security/security_profile/activity_tree/activity_tree_graph.go +++ b/pkg/security/security_profile/activity_tree/activity_tree_graph.go @@ -37,7 +37,7 @@ var ( ) // PrepareGraphData returns a graph from the activity tree -func (at *ActivityTree) PrepareGraphData(title string, resolver *process.Resolver) utils.Graph { +func (at *ActivityTree) PrepareGraphData(title string, resolver *process.EBPFResolver) utils.Graph { data := utils.Graph{ Title: title, Nodes: make(map[utils.GraphID]*utils.Node), @@ -50,7 +50,7 @@ func (at *ActivityTree) PrepareGraphData(title string, resolver *process.Resolve return data } -func (at *ActivityTree) prepareProcessNode(p *ProcessNode, data *utils.Graph, resolver *process.Resolver) utils.GraphID { +func (at *ActivityTree) prepareProcessNode(p *ProcessNode, data *utils.Graph, resolver *process.EBPFResolver) utils.GraphID { var args string var argv []string if resolver != nil { diff --git a/pkg/security/security_profile/activity_tree/file_node.go b/pkg/security/security_profile/activity_tree/file_node.go index 4c1cfdd11c39b..67565be114f6d 100644 --- a/pkg/security/security_profile/activity_tree/file_node.go +++ b/pkg/security/security_profile/activity_tree/file_node.go @@ -41,7 +41,7 @@ type OpenNode struct { } // NewFileNode returns a new FileActivityNode instance -func NewFileNode(fileEvent *model.FileEvent, event *model.Event, name string, generationType NodeGenerationType, reducedFilePath string, resolvers *resolvers.Resolvers) *FileNode { +func NewFileNode(fileEvent *model.FileEvent, event *model.Event, name string, generationType NodeGenerationType, reducedFilePath string, resolvers *resolvers.EBPFResolvers) *FileNode { // call resolver. Safeguard: the process context might be empty if from a snapshot. if resolvers != nil && fileEvent != nil && event.ProcessContext != nil { resolvers.HashResolver.ComputeHashesFromEvent(event, fileEvent) @@ -87,7 +87,7 @@ func (fn *FileNode) enrichFromEvent(event *model.Event) { return } if fn.FirstSeen.IsZero() { - fn.FirstSeen = event.FieldHandlers.ResolveEventTime(event) + fn.FirstSeen = event.ResolveEventTime() } fn.MatchedRules = model.AppendMatchedRule(fn.MatchedRules, event.Rules) @@ -126,7 +126,7 @@ func (fn *FileNode) debug(w io.Writer, prefix string) { // InsertFileEvent inserts an event in a FileNode. This function returns true if a new entry was added, false if // the event was dropped. -func (fn *FileNode) InsertFileEvent(fileEvent *model.FileEvent, event *model.Event, remainingPath string, generationType NodeGenerationType, stats *Stats, dryRun bool, reducedPath string, resolvers *resolvers.Resolvers) bool { +func (fn *FileNode) InsertFileEvent(fileEvent *model.FileEvent, event *model.Event, remainingPath string, generationType NodeGenerationType, stats *Stats, dryRun bool, reducedPath string, resolvers *resolvers.EBPFResolvers) bool { currentFn := fn currentPath := remainingPath newEntry := false diff --git a/pkg/security/security_profile/activity_tree/process_node.go b/pkg/security/security_profile/activity_tree/process_node.go index b7bc7c1b1bcbb..a2c0e190fb678 100644 --- a/pkg/security/security_profile/activity_tree/process_node.go +++ b/pkg/security/security_profile/activity_tree/process_node.go @@ -43,7 +43,7 @@ type ProcessNode struct { } // NewProcessNode returns a new ProcessNode instance -func NewProcessNode(entry *model.ProcessCacheEntry, generationType NodeGenerationType, resolvers *resolvers.Resolvers) *ProcessNode { +func NewProcessNode(entry *model.ProcessCacheEntry, generationType NodeGenerationType, resolvers *resolvers.EBPFResolvers) *ProcessNode { // call the callback to resolve additional fields before copying them if resolvers != nil { resolvers.HashResolver.ComputeHashes(model.ExecEventType, &entry.ProcessContext.Process, &entry.ProcessContext.FileEvent) @@ -135,7 +135,7 @@ func (pn *ProcessNode) debug(w io.Writer, prefix string) { } // scrubAndReleaseArgsEnvs scrubs the process args and envs, and then releases them -func (pn *ProcessNode) scrubAndReleaseArgsEnvs(resolver *sprocess.Resolver) { +func (pn *ProcessNode) scrubAndReleaseArgsEnvs(resolver *sprocess.EBPFResolver) { if pn.Process.ArgsEntry != nil { resolver.GetProcessArgvScrubbed(&pn.Process) sprocess.GetProcessArgv0(&pn.Process) @@ -203,7 +203,7 @@ newSyscallLoop: // InsertFileEvent inserts the provided file event in the current node. This function returns true if a new entry was // added, false if the event was dropped. -func (pn *ProcessNode) InsertFileEvent(fileEvent *model.FileEvent, event *model.Event, generationType NodeGenerationType, stats *Stats, dryRun bool, reducer *PathsReducer, resolvers *resolvers.Resolvers) bool { +func (pn *ProcessNode) InsertFileEvent(fileEvent *model.FileEvent, event *model.Event, generationType NodeGenerationType, stats *Stats, dryRun bool, reducer *PathsReducer, resolvers *resolvers.EBPFResolvers) bool { var filePath string if generationType != Snapshot { filePath = event.FieldHandlers.ResolveFilePath(event, fileEvent) diff --git a/pkg/security/security_profile/dump/graph.go b/pkg/security/security_profile/dump/graph.go index 5acb9ed9e6e23..5a82ce371f5ae 100644 --- a/pkg/security/security_profile/dump/graph.go +++ b/pkg/security/security_profile/dump/graph.go @@ -46,7 +46,7 @@ func (ad *ActivityDump) ToGraph() utils.Graph { defer ad.Unlock() title := fmt.Sprintf("%s: %s", ad.Metadata.Name, ad.getSelectorStr()) - var resolver *process.Resolver + var resolver *process.EBPFResolver if ad.adm != nil { resolver = ad.adm.resolvers.ProcessResolver } diff --git a/pkg/security/security_profile/dump/manager.go b/pkg/security/security_profile/dump/manager.go index 92fa8de62af75..e41282975fdff 100644 --- a/pkg/security/security_profile/dump/manager.go +++ b/pkg/security/security_profile/dump/manager.go @@ -58,7 +58,7 @@ type ActivityDumpManager struct { emptyDropped *atomic.Uint64 dropMaxDumpReached *atomic.Uint64 newEvent func() *model.Event - resolvers *resolvers.Resolvers + resolvers *resolvers.EBPFResolvers kernelVersion *kernel.Version manager *manager.Manager dumpHandler ActivityDumpHandler @@ -252,7 +252,7 @@ func (adm *ActivityDumpManager) HandleActivityDump(dump *api.ActivityDumpStreamM } // NewActivityDumpManager returns a new ActivityDumpManager instance -func NewActivityDumpManager(config *config.Config, statsdClient statsd.ClientInterface, newEvent func() *model.Event, resolvers *resolvers.Resolvers, +func NewActivityDumpManager(config *config.Config, statsdClient statsd.ClientInterface, newEvent func() *model.Event, resolvers *resolvers.EBPFResolvers, kernelVersion *kernel.Version, manager *manager.Manager) (*ActivityDumpManager, error) { tracedPIDs, err := managerhelper.Map(manager, "traced_pids") if err != nil { diff --git a/pkg/security/security_profile/profile/manager.go b/pkg/security/security_profile/profile/manager.go index 9cb5517a53faa..35d0136e2bd2f 100644 --- a/pkg/security/security_profile/profile/manager.go +++ b/pkg/security/security_profile/profile/manager.go @@ -121,7 +121,7 @@ type ActivityDumpManager interface { type SecurityProfileManager struct { config *config.Config statsdClient statsd.ClientInterface - resolvers *resolvers.Resolvers + resolvers *resolvers.EBPFResolvers providers []Provider activityDumpManager ActivityDumpManager @@ -143,7 +143,7 @@ type SecurityProfileManager struct { } // NewSecurityProfileManager returns a new instance of SecurityProfileManager -func NewSecurityProfileManager(config *config.Config, statsdClient statsd.ClientInterface, resolvers *resolvers.Resolvers, manager *manager.Manager) (*SecurityProfileManager, error) { +func NewSecurityProfileManager(config *config.Config, statsdClient statsd.ClientInterface, resolvers *resolvers.EBPFResolvers, manager *manager.Manager) (*SecurityProfileManager, error) { profileCache, err := simplelru.NewLRU[cgroupModel.WorkloadSelector, *SecurityProfile](config.RuntimeSecurity.SecurityProfileCacheSize, nil) if err != nil { return nil, fmt.Errorf("couldn't create security profile cache: %w", err) diff --git a/pkg/security/serializers/serializers_base.go b/pkg/security/serializers/serializers_base.go index 4c2365ec21232..c8b1f3c0b9d71 100644 --- a/pkg/security/serializers/serializers_base.go +++ b/pkg/security/serializers/serializers_base.go @@ -8,7 +8,6 @@ package serializers import ( - "github.com/DataDog/datadog-agent/pkg/security/resolvers" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/utils" ) @@ -209,7 +208,7 @@ func newExitEventSerializer(e *model.Event) *ExitEventSerializer { } // NewBaseEventSerializer creates a new event serializer based on the event type -func NewBaseEventSerializer(event *model.Event, resolvers *resolvers.Resolvers) *BaseEventSerializer { +func NewBaseEventSerializer(event *model.Event) *BaseEventSerializer { pc := event.ProcessContext eventType := model.EventType(event.Type) @@ -218,8 +217,8 @@ func NewBaseEventSerializer(event *model.Event, resolvers *resolvers.Resolvers) EventContextSerializer: EventContextSerializer{ Name: eventType.String(), }, - ProcessContextSerializer: newProcessContextSerializer(pc, event, resolvers), - Date: utils.NewEasyjsonTime(event.FieldHandlers.ResolveEventTime(event)), + ProcessContextSerializer: newProcessContextSerializer(pc, event), + Date: utils.NewEasyjsonTime(event.ResolveEventTime()), } if event.IsAnomalyDetectionEvent() && len(event.Rules) > 0 { diff --git a/pkg/security/serializers/serializers_linux.go b/pkg/security/serializers/serializers_linux.go index 846157f6b3e50..ee08063a51a45 100644 --- a/pkg/security/serializers/serializers_linux.go +++ b/pkg/security/serializers/serializers_linux.go @@ -18,7 +18,6 @@ import ( "golang.org/x/sys/unix" "github.com/DataDog/datadog-agent/pkg/security/events" - "github.com/DataDog/datadog-agent/pkg/security/resolvers" sprocess "github.com/DataDog/datadog-agent/pkg/security/resolvers/process" "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" "github.com/DataDog/datadog-agent/pkg/security/secl/model" @@ -439,7 +438,7 @@ type MountEventSerializer struct { // Mount source path MountSourcePath string `json:"source.path,omitempty"` // Mount point path error - MountPointPathResolutionError string `json:"mountpoint.path_error,omitempty"` + MountRootPathResolutionError string `json:"mountpoint.path_error,omitempty"` // Mount source path error MountSourcePathResolutionError string `json:"source.path_error,omitempty"` } @@ -557,10 +556,10 @@ func newCredentialsSerializer(ce *model.Credentials) *CredentialsSerializer { } } -func newProcessSerializer(ps *model.Process, e *model.Event, resolvers *resolvers.Resolvers) *ProcessSerializer { +func newProcessSerializer(ps *model.Process, e *model.Event) *ProcessSerializer { if ps.IsNotKworker() { - argv, argvTruncated := resolvers.ProcessResolver.GetProcessArgvScrubbed(ps) - envs, EnvsTruncated := resolvers.ProcessResolver.GetProcessEnvs(ps) + argv, argvTruncated := e.GetProcessArgvScrubbed(), e.GetProcessArgsTruncated() + envs, EnvsTruncated := e.GetProcessEnvs(), e.GetProcessEnvsTruncated() argv0, _ := sprocess.GetProcessArgv0(ps) psSerializer := &ProcessSerializer{ @@ -600,15 +599,13 @@ func newProcessSerializer(ps *model.Process, e *model.Event, resolvers *resolver } if ps.UserSession.ID != 0 { - psSerializer.UserSession = newUserSessionContextSerializer(&ps.UserSession, e, resolvers) + psSerializer.UserSession = newUserSessionContextSerializer(&ps.UserSession, e) } if len(ps.ContainerID) != 0 { psSerializer.Container = &ContainerContextSerializer{ - ID: ps.ContainerID, - } - if cgroup, _ := resolvers.CGroupResolver.GetWorkload(ps.ContainerID); cgroup != nil { - psSerializer.Container.CreatedAt = getTimeIfNotZero(time.Unix(0, int64(cgroup.CreatedAt))) + ID: ps.ContainerID, + CreatedAt: getTimeIfNotZero(time.Unix(0, int64(e.GetContainerCreatedAt()))), } } return psSerializer @@ -625,7 +622,7 @@ func newProcessSerializer(ps *model.Process, e *model.Event, resolvers *resolver } } -func newUserSessionContextSerializer(ctx *model.UserSessionContext, e *model.Event, _ *resolvers.Resolvers) *UserSessionContextSerializer { +func newUserSessionContextSerializer(ctx *model.UserSessionContext, e *model.Event) *UserSessionContextSerializer { return &UserSessionContextSerializer{ ID: fmt.Sprintf("%x", ctx.ID), K8SUsername: e.FieldHandlers.ResolveK8SUsername(e, ctx), @@ -720,11 +717,11 @@ func newMProtectEventSerializer(e *model.Event) *MProtectEventSerializer { } } -func newPTraceEventSerializer(e *model.Event, resolvers *resolvers.Resolvers) *PTraceEventSerializer { +func newPTraceEventSerializer(e *model.Event) *PTraceEventSerializer { return &PTraceEventSerializer{ Request: model.PTraceRequest(e.PTrace.Request).String(), Address: fmt.Sprintf("0x%x", e.PTrace.Address), - Tracee: newProcessContextSerializer(e.PTrace.Tracee, e, resolvers), + Tracee: newProcessContextSerializer(e.PTrace.Tracee, e), } } @@ -745,11 +742,11 @@ func newUnloadModuleEventSerializer(e *model.Event) *ModuleEventSerializer { } } -func newSignalEventSerializer(e *model.Event, resolvers *resolvers.Resolvers) *SignalEventSerializer { +func newSignalEventSerializer(e *model.Event) *SignalEventSerializer { ses := &SignalEventSerializer{ Type: model.Signal(e.Signal.Type).String(), PID: e.Signal.PID, - Target: newProcessContextSerializer(e.Signal.Target, e, resolvers), + Target: newProcessContextSerializer(e.Signal.Target, e), } return ses } @@ -769,22 +766,23 @@ func newBindEventSerializer(e *model.Event) *BindEventSerializer { return bes } -func newMountEventSerializer(e *model.Event, resolvers *resolvers.Resolvers) *MountEventSerializer { +func newMountEventSerializer(e *model.Event) *MountEventSerializer { fh := e.FieldHandlers - src, srcErr := resolvers.PathResolver.ResolveMountRoot(e, &e.Mount.Mount) - dst, dstErr := resolvers.PathResolver.ResolveMountPoint(e, &e.Mount.Mount) + //src, srcErr := , e.Mount.MountPointPathResolutionError + //resolvers.PathResolver.ResolveMountRoot(e, &e.Mount.Mount) + //dst, dstErr := resolvers.PathResolver.ResolveMountPoint(e, &e.Mount.Mount) mountPointPath := fh.ResolveMountPointPath(e, &e.Mount) mountSourcePath := fh.ResolveMountSourcePath(e, &e.Mount) mountSerializer := &MountEventSerializer{ MountPoint: &FileSerializer{ - Path: dst, + Path: e.GetMountRootPath(), MountID: &e.Mount.ParentPathKey.MountID, Inode: &e.Mount.ParentPathKey.Inode, }, Root: &FileSerializer{ - Path: src, + Path: e.GetMountMountpointPath(), MountID: &e.Mount.RootPathKey.MountID, Inode: &e.Mount.RootPathKey.Inode, }, @@ -797,15 +795,9 @@ func newMountEventSerializer(e *model.Event, resolvers *resolvers.Resolvers) *Mo MountSourcePath: mountSourcePath, } - if srcErr != nil { - mountSerializer.Root.PathResolutionError = srcErr.Error() - } - if dstErr != nil { - mountSerializer.MountPoint.PathResolutionError = dstErr.Error() - } // potential errors retrieved from ResolveMountPointPath and ResolveMountSourcePath - if e.Mount.MountPointPathResolutionError != nil { - mountSerializer.MountPointPathResolutionError = e.Mount.MountPointPathResolutionError.Error() + if e.Mount.MountRootPathResolutionError != nil { + mountSerializer.MountRootPathResolutionError = e.Mount.MountRootPathResolutionError.Error() } if e.Mount.MountSourcePathResolutionError != nil { mountSerializer.MountSourcePathResolutionError = e.Mount.MountSourcePathResolutionError.Error() @@ -834,13 +826,13 @@ func serializeOutcome(retval int64) string { } } -func newProcessContextSerializer(pc *model.ProcessContext, e *model.Event, resolvers *resolvers.Resolvers) *ProcessContextSerializer { +func newProcessContextSerializer(pc *model.ProcessContext, e *model.Event) *ProcessContextSerializer { if pc == nil || pc.Pid == 0 || e == nil { return nil } ps := ProcessContextSerializer{ - ProcessSerializer: newProcessSerializer(&pc.Process, e, resolvers), + ProcessSerializer: newProcessSerializer(&pc.Process, e), } ctx := eval.NewContext(e) @@ -856,7 +848,7 @@ func newProcessContextSerializer(pc *model.ProcessContext, e *model.Event, resol for ptr != nil { pce := (*model.ProcessCacheEntry)(ptr) - s := newProcessSerializer(&pce.Process, e, resolvers) + s := newProcessSerializer(&pce.Process, e) ps.Ancestors = append(ps.Ancestors, s) if first { @@ -941,8 +933,8 @@ func (e *EventSerializer) MarshalJSON() ([]byte, error) { } // MarshalEvent marshal the event -func MarshalEvent(event *model.Event, probe *resolvers.Resolvers) ([]byte, error) { - s := NewEventSerializer(event, probe) +func MarshalEvent(event *model.Event) ([]byte, error) { + s := NewEventSerializer(event) return utils.MarshalEasyJSON(s) } @@ -952,9 +944,9 @@ func MarshalCustomEvent(event *events.CustomEvent) ([]byte, error) { } // NewEventSerializer creates a new event serializer based on the event type -func NewEventSerializer(event *model.Event, resolvers *resolvers.Resolvers) *EventSerializer { +func NewEventSerializer(event *model.Event) *EventSerializer { s := &EventSerializer{ - BaseEventSerializer: NewBaseEventSerializer(event, resolvers), + BaseEventSerializer: NewBaseEventSerializer(event), UserContextSerializer: newUserContextSerializer(event), DDContextSerializer: newDDContextSerializer(event), } @@ -968,14 +960,10 @@ func NewEventSerializer(event *model.Event, resolvers *resolvers.Resolvers) *Eve s.SecurityProfileContextSerializer = newSecurityProfileContextSerializer(&event.SecurityProfileContext) } - if id := event.FieldHandlers.ResolveContainerID(event, event.ContainerContext); id != "" { - var creationTime time.Time - if cgroup, _ := resolvers.CGroupResolver.GetWorkload(id); cgroup != nil { - creationTime = time.Unix(0, int64(cgroup.CreatedAt)) - } + if ctx, exists := event.FieldHandlers.ResolveContainerContext(event); exists { s.ContainerContextSerializer = &ContainerContextSerializer{ - ID: id, - CreatedAt: getTimeIfNotZero(creationTime), + ID: ctx.ID, + CreatedAt: getTimeIfNotZero(time.Unix(0, int64(ctx.CreatedAt))), } } @@ -1073,7 +1061,7 @@ func NewEventSerializer(event *model.Event, resolvers *resolvers.Resolvers) *Eve } s.EventContextSerializer.Outcome = serializeOutcome(event.Utimes.Retval) case model.FileMountEventType: - s.MountEventSerializer = newMountEventSerializer(event, resolvers) + s.MountEventSerializer = newMountEventSerializer(event) s.EventContextSerializer.Outcome = serializeOutcome(event.Mount.Retval) case model.FileUmountEventType: s.FileEventSerializer = &FileEventSerializer{ @@ -1130,7 +1118,7 @@ func NewEventSerializer(event *model.Event, resolvers *resolvers.Resolvers) *Eve s.MProtectEventSerializer = newMProtectEventSerializer(event) case model.PTraceEventType: s.EventContextSerializer.Outcome = serializeOutcome(event.PTrace.Retval) - s.PTraceEventSerializer = newPTraceEventSerializer(event, resolvers) + s.PTraceEventSerializer = newPTraceEventSerializer(event) case model.LoadModuleEventType: s.EventContextSerializer.Outcome = serializeOutcome(event.LoadModule.Retval) if !event.LoadModule.LoadedFromMemory { @@ -1144,7 +1132,7 @@ func NewEventSerializer(event *model.Event, resolvers *resolvers.Resolvers) *Eve s.ModuleEventSerializer = newUnloadModuleEventSerializer(event) case model.SignalEventType: s.EventContextSerializer.Outcome = serializeOutcome(event.Signal.Retval) - s.SignalEventSerializer = newSignalEventSerializer(event, resolvers) + s.SignalEventSerializer = newSignalEventSerializer(event) case model.SpliceEventType: s.EventContextSerializer.Outcome = serializeOutcome(event.Splice.Retval) s.SpliceEventSerializer = newSpliceEventSerializer(event) diff --git a/pkg/security/serializers/serializers_linux_easyjson.go b/pkg/security/serializers/serializers_linux_easyjson.go index 138f5eaf7b7a3..fd93d5c617a4d 100644 --- a/pkg/security/serializers/serializers_linux_easyjson.go +++ b/pkg/security/serializers/serializers_linux_easyjson.go @@ -1687,7 +1687,7 @@ func easyjsonDdc0fdbeDecodeGithubComDataDogDatadogAgentPkgSecuritySerializers14( case "source.path": out.MountSourcePath = string(in.String()) case "mountpoint.path_error": - out.MountPointPathResolutionError = string(in.String()) + out.MountRootPathResolutionError = string(in.String()) case "source.path_error": out.MountSourcePathResolutionError = string(in.String()) default: @@ -1760,10 +1760,10 @@ func easyjsonDdc0fdbeEncodeGithubComDataDogDatadogAgentPkgSecuritySerializers14( out.RawString(prefix) out.String(string(in.MountSourcePath)) } - if in.MountPointPathResolutionError != "" { + if in.MountRootPathResolutionError != "" { const prefix string = ",\"mountpoint.path_error\":" out.RawString(prefix) - out.String(string(in.MountPointPathResolutionError)) + out.String(string(in.MountRootPathResolutionError)) } if in.MountSourcePathResolutionError != "" { const prefix string = ",\"source.path_error\":" diff --git a/pkg/security/serializers/serializers_others.go b/pkg/security/serializers/serializers_others.go index 5e3614cbc2f1a..296bf4ca362af 100644 --- a/pkg/security/serializers/serializers_others.go +++ b/pkg/security/serializers/serializers_others.go @@ -12,7 +12,6 @@ import ( json "encoding/json" "github.com/DataDog/datadog-agent/pkg/security/events" - "github.com/DataDog/datadog-agent/pkg/security/resolvers" "github.com/DataDog/datadog-agent/pkg/security/secl/model" ) @@ -25,8 +24,8 @@ func (e *EventSerializer) ToJSON() ([]byte, error) { } // MarshalEvent marshal the event -func MarshalEvent(event *model.Event, probe *resolvers.Resolvers) ([]byte, error) { - s := NewEventSerializer(event, probe) +func MarshalEvent(event *model.Event) ([]byte, error) { + s := NewEventSerializer(event) return json.Marshal(s) } @@ -36,6 +35,6 @@ func MarshalCustomEvent(event *events.CustomEvent) ([]byte, error) { } // NewEventSerializer creates a new event serializer based on the event type -func NewEventSerializer(event *model.Event, resolvers *resolvers.Resolvers) *EventSerializer { //nolint:revive // TODO fix revive unused-parameter +func NewEventSerializer(_ *model.Event) *EventSerializer { return nil } diff --git a/pkg/security/serializers/serializers_windows.go b/pkg/security/serializers/serializers_windows.go index b02378f4276b1..7663eb072e30c 100644 --- a/pkg/security/serializers/serializers_windows.go +++ b/pkg/security/serializers/serializers_windows.go @@ -9,7 +9,6 @@ import ( "encoding/json" "github.com/DataDog/datadog-agent/pkg/security/events" - "github.com/DataDog/datadog-agent/pkg/security/resolvers" "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/utils" @@ -62,7 +61,7 @@ func newFileSerializer(fe *model.FileEvent, e *model.Event, forceInode ...uint64 } } -func newProcessSerializer(ps *model.Process, e *model.Event, resolvers *resolvers.Resolvers) *ProcessSerializer { //nolint:revive // TODO fix revive unused-parameter +func newProcessSerializer(ps *model.Process, e *model.Event) *ProcessSerializer { //nolint:revive // TODO fix revive unused-parameter psSerializer := &ProcessSerializer{ ExecTime: getTimeIfNotZero(ps.ExecTime), ExitTime: getTimeIfNotZero(ps.ExitTime), @@ -70,7 +69,7 @@ func newProcessSerializer(ps *model.Process, e *model.Event, resolvers *resolver Pid: ps.Pid, PPid: getUint32Pointer(&ps.PPid), Executable: newFileSerializer(&ps.FileEvent, e), - CmdLine: resolvers.ProcessResolver.GetProcessCmdLineScrubbed(ps), + // CmdLine: e.GetProcessCmdLineScrubbed(ps), } if len(ps.ContainerID) != 0 { @@ -81,13 +80,13 @@ func newProcessSerializer(ps *model.Process, e *model.Event, resolvers *resolver return psSerializer } -func newProcessContextSerializer(pc *model.ProcessContext, e *model.Event, resolvers *resolvers.Resolvers) *ProcessContextSerializer { +func newProcessContextSerializer(pc *model.ProcessContext, e *model.Event) *ProcessContextSerializer { if pc == nil || pc.Pid == 0 || e == nil { return nil } ps := ProcessContextSerializer{ - ProcessSerializer: newProcessSerializer(&pc.Process, e, resolvers), + ProcessSerializer: newProcessSerializer(&pc.Process, e), } ctx := eval.NewContext(e) @@ -100,7 +99,7 @@ func newProcessContextSerializer(pc *model.ProcessContext, e *model.Event, resol for ptr != nil { pce := (*model.ProcessCacheEntry)(ptr) - s := newProcessSerializer(&pce.Process, e, resolvers) + s := newProcessSerializer(&pce.Process, e) ps.Ancestors = append(ps.Ancestors, s) if first { @@ -124,8 +123,8 @@ func (e *EventSerializer) ToJSON() ([]byte, error) { } // MarshalEvent marshal the event -func MarshalEvent(event *model.Event, probe *resolvers.Resolvers) ([]byte, error) { - s := NewEventSerializer(event, probe) +func MarshalEvent(event *model.Event) ([]byte, error) { + s := NewEventSerializer(event) return json.Marshal(s) } @@ -135,8 +134,8 @@ func MarshalCustomEvent(event *events.CustomEvent) ([]byte, error) { } // NewEventSerializer creates a new event serializer based on the event type -func NewEventSerializer(event *model.Event, resolvers *resolvers.Resolvers) *EventSerializer { +func NewEventSerializer(event *model.Event) *EventSerializer { return &EventSerializer{ - BaseEventSerializer: NewBaseEventSerializer(event, resolvers), + BaseEventSerializer: NewBaseEventSerializer(event), } } diff --git a/pkg/security/tests/dentry_test.go b/pkg/security/tests/dentry_test.go index 0a717ca066313..03ab6db26cda4 100644 --- a/pkg/security/tests/dentry_test.go +++ b/pkg/security/tests/dentry_test.go @@ -19,56 +19,57 @@ import ( "github.com/DataDog/datadog-agent/pkg/security/ebpf/kernel" "github.com/DataDog/datadog-agent/pkg/security/metrics" + sprobe "github.com/DataDog/datadog-agent/pkg/security/probe" "github.com/DataDog/datadog-agent/pkg/security/resolvers/dentry" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/secl/rules" ) -func validateResolution(test *testModule, event *model.Event, testFile string, pathFnc func(model.PathKey, bool) (string, error), parentFnc func(model.PathKey) (model.PathKey, error), nameFnc func(model.PathKey) (string, error)) { +func validateResolution(t testing.TB, probe *sprobe.EBPFProbe, event *model.Event, testFile string, pathFnc func(model.PathKey, bool) (string, error), parentFnc func(model.PathKey) (model.PathKey, error), nameFnc func(model.PathKey) (string, error)) { basename := path.Base(testFile) // Force an eRPC resolution to refresh the entry with the last generation as lost events may have invalidated the entry res, err := pathFnc(event.Open.File.PathKey, true) - assert.Nil(test.t, err) - assert.Equal(test.t, basename, path.Base(res)) + assert.Nil(t, err) + assert.Equal(t, basename, path.Base(res)) // there is a potential race here as a lost event can occur between the two resolutions // check that the path is now available from the cache - res, err = test.probe.GetResolvers().DentryResolver.ResolveFromCache(event.Open.File.PathKey) - assert.Nil(test.t, err) - assert.Equal(test.t, basename, path.Base(res)) + res, err = probe.Resolvers.DentryResolver.ResolveFromCache(event.Open.File.PathKey) + assert.Nil(t, err) + assert.Equal(t, basename, path.Base(res)) kv, err := kernel.NewKernelVersion() - assert.Nil(test.t, err) + assert.Nil(t, err) // Parent - expectedInode := getInode(test.t, path.Dir(testFile)) + expectedInode := getInode(t, path.Dir(testFile)) // the previous path resolution should habe filled the cache - pathKey, err := test.probe.GetResolvers().DentryResolver.ResolveParentFromCache(event.Open.File.PathKey) - assert.Nil(test.t, err) - assert.NotZero(test.t, pathKey.Inode) + pathKey, err := probe.Resolvers.DentryResolver.ResolveParentFromCache(event.Open.File.PathKey) + assert.Nil(t, err) + assert.NotZero(t, pathKey.Inode) // on kernel < 5.0 the cache is populated with internal inode of overlayfs. The stat syscall returns the proper inode, that is why the inodes don't match. if event.Open.File.Filesystem != model.OverlayFS || kv.Code > kernel.Kernel5_0 { - assert.Equal(test.t, expectedInode, pathKey.Inode) + assert.Equal(t, expectedInode, pathKey.Inode) } parentKey, err := parentFnc(event.Open.File.PathKey) - assert.Nil(test.t, err) - assert.NotZero(test.t, parentKey.Inode) - assert.Equal(test.t, pathKey.Inode, parentKey.Inode) + assert.Nil(t, err) + assert.NotZero(t, parentKey.Inode) + assert.Equal(t, pathKey.Inode, parentKey.Inode) // Basename // the previous path resolution should have filled the cache - expectedName, err := test.probe.GetResolvers().DentryResolver.ResolveNameFromCache(event.Open.File.PathKey) - assert.Nil(test.t, err) - assert.Equal(test.t, expectedName, basename) + expectedName, err := probe.Resolvers.DentryResolver.ResolveNameFromCache(event.Open.File.PathKey) + assert.Nil(t, err) + assert.Equal(t, expectedName, basename) expectedName, err = nameFnc(event.Open.File.PathKey) - assert.Nil(test.t, err) - assert.Equal(test.t, expectedName, basename) + assert.Nil(t, err) + assert.Equal(t, expectedName, basename) } func TestDentryResolutionERPC(t *testing.T) { @@ -88,6 +89,11 @@ func TestDentryResolutionERPC(t *testing.T) { } defer test.Close() + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + t.Skip("not supported") + } + testFile, _, err := test.Path("parent/" + basename) if err != nil { t.Fatal(err) @@ -113,10 +119,10 @@ func TestDentryResolutionERPC(t *testing.T) { key = metrics.MetricDentryResolverHits + ":" + metrics.KernelMapsTag assert.Empty(t, test.statsdClient.Get(key)) - validateResolution(test, event, testFile, - test.probe.GetResolvers().DentryResolver.ResolveFromERPC, - test.probe.GetResolvers().DentryResolver.ResolveParentFromERPC, - test.probe.GetResolvers().DentryResolver.ResolveNameFromERPC, + validateResolution(test.t, p, event, testFile, + p.Resolvers.DentryResolver.ResolveFromERPC, + p.Resolvers.DentryResolver.ResolveParentFromERPC, + p.Resolvers.DentryResolver.ResolveNameFromERPC, ) }) } @@ -138,6 +144,11 @@ func TestDentryResolutionMap(t *testing.T) { } defer test.Close() + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + t.Skip("not supported") + } + testFile, _, err := test.Path("parent/" + basename) if err != nil { t.Fatal(err) @@ -167,10 +178,10 @@ func TestDentryResolutionMap(t *testing.T) { key = metrics.MetricDentryResolverHits + ":" + metrics.ERPCTag assert.Empty(t, test.statsdClient.Get(key)) - validateResolution(test, event, testFile, - test.probe.GetResolvers().DentryResolver.ResolveFromMap, - test.probe.GetResolvers().DentryResolver.ResolveParentFromMap, - test.probe.GetResolvers().DentryResolver.ResolveNameFromMap, + validateResolution(test.t, p, event, testFile, + p.Resolvers.DentryResolver.ResolveFromMap, + p.Resolvers.DentryResolver.ResolveParentFromMap, + p.Resolvers.DentryResolver.ResolveNameFromMap, ) }) } @@ -187,6 +198,11 @@ func BenchmarkERPCDentryResolutionSegment(b *testing.B) { } defer test.Close() + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + b.Skip("not supported") + } + testFile, _, err := test.Path("aa/bb/cc/dd/ee") if err != nil { b.Fatal(err) @@ -211,12 +227,12 @@ func BenchmarkERPCDentryResolutionSegment(b *testing.B) { } // create a new dentry resolver to avoid concurrent map access errors - resolver, err := dentry.NewResolver(test.probe.Config.Probe, test.probe.StatsdClient, test.probe.Erpc) + resolver, err := dentry.NewResolver(test.probe.Config.Probe, test.probe.StatsdClient, p.Erpc) if err != nil { b.Fatal(err) } - if err := resolver.Start(test.probe.Manager); err != nil { + if err := resolver.Start(p.Manager); err != nil { b.Fatal(err) } name, err := resolver.ResolveNameFromERPC(pathKey) @@ -251,6 +267,11 @@ func BenchmarkERPCDentryResolutionPath(b *testing.B) { } defer test.Close() + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + b.Skip("not supported") + } + testFile, _, err := test.Path("aa/bb/cc/dd/ee") if err != nil { b.Fatal(err) @@ -275,12 +296,12 @@ func BenchmarkERPCDentryResolutionPath(b *testing.B) { } // create a new dentry resolver to avoid concurrent map access errors - resolver, err := dentry.NewResolver(test.probe.Config.Probe, test.probe.StatsdClient, test.probe.Erpc) + resolver, err := dentry.NewResolver(test.probe.Config.Probe, test.probe.StatsdClient, p.Erpc) if err != nil { b.Fatal(err) } - if err := resolver.Start(test.probe.Manager); err != nil { + if err := resolver.Start(p.Manager); err != nil { b.Fatal(err) } f, err := resolver.ResolveFromERPC(pathKey, true) @@ -315,6 +336,11 @@ func BenchmarkMapDentryResolutionSegment(b *testing.B) { } defer test.Close() + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + b.Skip("not supported") + } + testFile, _, err := test.Path("aa/bb/cc/dd/ee") if err != nil { b.Fatal(err) @@ -339,12 +365,12 @@ func BenchmarkMapDentryResolutionSegment(b *testing.B) { } // create a new dentry resolver to avoid concurrent map access errors - resolver, err := dentry.NewResolver(test.probe.Config.Probe, test.probe.StatsdClient, test.probe.Erpc) + resolver, err := dentry.NewResolver(test.probe.Config.Probe, test.probe.StatsdClient, p.Erpc) if err != nil { b.Fatal(err) } - if err := resolver.Start(test.probe.Manager); err != nil { + if err := resolver.Start(p.Manager); err != nil { b.Fatal(err) } name, err := resolver.ResolveNameFromMap(pathKey) @@ -379,6 +405,11 @@ func BenchmarkMapDentryResolutionPath(b *testing.B) { } defer test.Close() + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + b.Skip("not supported") + } + testFile, _, err := test.Path("aa/bb/cc/dd/ee") if err != nil { b.Fatal(err) @@ -402,12 +433,12 @@ func BenchmarkMapDentryResolutionPath(b *testing.B) { } // create a new dentry resolver to avoid concurrent map access errors - resolver, err := dentry.NewResolver(test.probe.Config.Probe, test.probe.StatsdClient, test.probe.Erpc) + resolver, err := dentry.NewResolver(test.probe.Config.Probe, test.probe.StatsdClient, p.Erpc) if err != nil { b.Fatal(err) } - if err := resolver.Start(test.probe.Manager); err != nil { + if err := resolver.Start(p.Manager); err != nil { b.Fatal(err) } f, err := resolver.ResolveFromMap(pathKey, true) diff --git a/pkg/security/tests/module_tester.go b/pkg/security/tests/module_tester.go index 76029ad01ed33..8fe3ab23f91cd 100644 --- a/pkg/security/tests/module_tester.go +++ b/pkg/security/tests/module_tester.go @@ -525,7 +525,7 @@ func assertFieldStringArrayIndexedOneOf(tb *testing.T, e *model.Event, field str //nolint:deadcode,unused func validateProcessContextLineage(tb testing.TB, event *model.Event, probe *sprobe.Probe) { - eventJSON, err := serializers.MarshalEvent(event, probe.GetResolvers()) + eventJSON, err := serializers.MarshalEvent(event) if err != nil { tb.Errorf("failed to marshal event: %v", err) return @@ -614,7 +614,7 @@ func validateProcessContextSECL(tb testing.TB, event *model.Event, probe *sprobe valid := nameFieldValid && pathFieldValid if !valid { - eventJSON, err := serializers.MarshalEvent(event, probe.GetResolvers()) + eventJSON, err := serializers.MarshalEvent(event) if err != nil { tb.Errorf("failed to marshal event: %v", err) return @@ -955,7 +955,7 @@ func newTestModule(t testing.TB, macroDefs []*rules.MacroDefinition, ruleDefs [] } if ruleDefs != nil && logStatusMetrics { - t.Logf("%s entry stats: %s\n", t.Name(), GetStatusMetrics(testMod.probe)) + t.Logf("%s entry stats: %s\n", t.Name(), GetEBPFStatusMetrics(testMod.probe)) } return testMod, nil } else if testMod != nil { @@ -1028,7 +1028,7 @@ func newTestModule(t testing.TB, macroDefs []*rules.MacroDefinition, ruleDefs [] return nil, err } - testMod.probe.AddNewNotifyDiscarderPushedCallback(testMod.NotifyDiscarderPushedCallback) + testMod.probe.AddDiscarderPushedCallback(testMod.NotifyDiscarderPushedCallback) if err := testMod.eventMonitor.Init(); err != nil { return nil, fmt.Errorf("failed to init module: %w", err) @@ -1036,7 +1036,12 @@ func newTestModule(t testing.TB, macroDefs []*rules.MacroDefinition, ruleDefs [] kv, _ := kernel.NewKernelVersion() - if os.Getenv("DD_TESTS_RUNTIME_COMPILED") == "1" && secconfig.Probe.RuntimeCompilationEnabled && !testMod.eventMonitor.Probe.IsRuntimeCompiled() && !kv.IsSuseKernel() { + var isRuntimeCompiled bool + if p, ok := testMod.eventMonitor.Probe.PlatformProbe.(*probe.EBPFProbe); ok { + isRuntimeCompiled = p.IsRuntimeCompiled() + } + + if os.Getenv("DD_TESTS_RUNTIME_COMPILED") == "1" && secconfig.Probe.RuntimeCompilationEnabled && !isRuntimeCompiled && !kv.IsSuseKernel() { return nil, errors.New("failed to runtime compile module") } @@ -1064,7 +1069,7 @@ func newTestModule(t testing.TB, macroDefs []*rules.MacroDefinition, ruleDefs [] } if logStatusMetrics { - t.Logf("%s entry stats: %s\n", t.Name(), GetStatusMetrics(testMod.probe)) + t.Logf("%s entry stats: %s\n", t.Name(), GetEBPFStatusMetrics(testMod.probe)) } return testMod, nil @@ -1197,7 +1202,7 @@ func (tm *testModule) GetEventDiscarder(tb testing.TB, action func() error, cb o //nolint:deadcode,unused func (tm *testModule) marshalEvent(ev *model.Event) (string, error) { - b, err := serializers.MarshalEvent(ev, tm.probe.GetResolvers()) + b, err := serializers.MarshalEvent(ev) return string(b), err } @@ -1210,12 +1215,18 @@ func (tm *testModule) debugEvent(ev *model.Event) string { return string(b) } -// GetStatusMetrics returns a string representation of the perf buffer monitor metrics -func GetStatusMetrics(probe *sprobe.Probe) string { +// GetEBPFStatusMetrics returns a string representation of the perf buffer monitor metrics +func GetEBPFStatusMetrics(probe *sprobe.Probe) string { if probe == nil { return "" } - monitors := probe.GetMonitors() + + p, ok := probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + return "" + } + + monitors := p.GetMonitors() if monitors == nil { return "" } @@ -1262,7 +1273,7 @@ func (tm *testModule) NewTimeoutError() ErrTimeout { var msg strings.Builder msg.WriteString("timeout, details: ") - msg.WriteString(GetStatusMetrics(tm.probe)) + msg.WriteString(GetEBPFStatusMetrics(tm.probe)) msg.WriteString(spew.Sdump(ddebpf.GetProbeStats())) events := tm.ruleEngine.StopEventCollector() @@ -1672,7 +1683,7 @@ func (tm *testModule) Close() { tm.statsdClient.Flush() if logStatusMetrics { - tm.t.Logf("%s exit stats: %s\n", tm.t.Name(), GetStatusMetrics(tm.probe)) + tm.t.Logf("%s exit stats: %s\n", tm.t.Name(), GetEBPFStatusMetrics(tm.probe)) } if withProfile { @@ -1894,11 +1905,16 @@ func checkKernelCompatibility(tb testing.TB, why string, skipCheck func(kv *kern } func (tm *testModule) StartActivityDumpComm(comm string, outputDir string, formats []string) ([]string, error) { - managers := tm.probe.GetProfileManagers() + p, ok := tm.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + return nil, errors.New("not supported") + } + + managers := p.GetProfileManagers() if managers == nil { - return nil, errors.New("No monitor") + return nil, errors.New("no manager") } - p := &api.ActivityDumpParams{ + params := &api.ActivityDumpParams{ Comm: comm, Timeout: "1m", DifferentiateArgs: true, @@ -1910,7 +1926,7 @@ func (tm *testModule) StartActivityDumpComm(comm string, outputDir string, forma RemoteStorageCompression: false, }, } - mess, err := managers.DumpActivity(p) + mess, err := managers.DumpActivity(params) if err != nil || mess == nil || len(mess.Storage) < 1 { return nil, fmt.Errorf("failed to start activity dump: err:%v message:%v len:%v", err, mess, len(mess.Storage)) } @@ -1923,16 +1939,21 @@ func (tm *testModule) StartActivityDumpComm(comm string, outputDir string, forma } func (tm *testModule) StopActivityDump(name, containerID, comm string) error { - managers := tm.probe.GetProfileManagers() + p, ok := tm.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + return errors.New("not supported") + } + + managers := p.GetProfileManagers() if managers == nil { - return errors.New("No monitor") + return errors.New("no manager") } - p := &api.ActivityDumpStopParams{ + params := &api.ActivityDumpStopParams{ Name: name, ContainerID: containerID, Comm: comm, } - _, err := managers.StopActivityDump(p) + _, err := managers.StopActivityDump(params) if err != nil { return err } @@ -1947,12 +1968,17 @@ type activityDumpIdentifier struct { } func (tm *testModule) ListActivityDumps() ([]*activityDumpIdentifier, error) { - managers := tm.probe.GetProfileManagers() + p, ok := tm.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + return nil, errors.New("not supported") + } + + managers := p.GetProfileManagers() if managers == nil { return nil, errors.New("No monitor") } - p := &api.ActivityDumpListParams{} - mess, err := managers.ListActivityDumps(p) + params := &api.ActivityDumpListParams{} + mess, err := managers.ListActivityDumps(params) if err != nil || mess == nil { return nil, err } @@ -1980,7 +2006,12 @@ func (tm *testModule) ListActivityDumps() ([]*activityDumpIdentifier, error) { } func (tm *testModule) DecodeActivityDump(path string) (*dump.ActivityDump, error) { - managers := tm.probe.GetProfileManagers() + p, ok := tm.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + return nil, errors.New("not supported") + } + + managers := p.GetProfileManagers() if managers == nil { return nil, errors.New("No monitor") } @@ -2121,7 +2152,12 @@ func (tm *testModule) addAllEventTypesOnDump(dockerInstance *dockerCmdWrapper, i //nolint:deadcode,unused func (tm *testModule) triggerLoadControllerReducer(dockerInstance *dockerCmdWrapper, id *activityDumpIdentifier) { - managers := tm.probe.GetProfileManagers() + p, ok := tm.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + return + } + + managers := p.GetProfileManagers() if managers == nil { return } @@ -2369,9 +2405,14 @@ func (tm *testModule) GetADSelector(dumpID *activityDumpIdentifier) (*cgroupMode } func (tm *testModule) SetProfileStatus(selector *cgroupModel.WorkloadSelector, newStatus model.Status) error { - managers := tm.probe.GetProfileManagers() + p, ok := tm.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + return errors.New("not supported") + } + + managers := p.GetProfileManagers() if managers == nil { - return errors.New("No monitor") + return errors.New("no manager") } spm := managers.GetSecurityProfileManager() diff --git a/pkg/security/tests/mount_test.go b/pkg/security/tests/mount_test.go index 90b2a6ff3d50a..5ba4d448aa7a5 100644 --- a/pkg/security/tests/mount_test.go +++ b/pkg/security/tests/mount_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/assert" "golang.org/x/sys/unix" + sprobe "github.com/DataDog/datadog-agent/pkg/security/probe" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/secl/rules" "github.com/DataDog/datadog-agent/pkg/security/utils" @@ -324,6 +325,11 @@ func TestMountSnapshot(t *testing.T) { } defer test.Close() + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + t.Skip("not supported") + } + tmpfsMountB, bindMountB, err := createHierarchy(rootB) if err != nil { t.Fatal(err) @@ -331,7 +337,7 @@ func TestMountSnapshot(t *testing.T) { defer tmpfsMountB.unmount(0) defer bindMountB.unmount(0) - mountResolver := test.probe.GetResolvers().MountResolver + mountResolver := p.Resolvers.MountResolver pid := utils.Getpid() mounts, err := kernel.ParseMountInfoFile(int32(pid)) diff --git a/pkg/security/tests/network_device_test.go b/pkg/security/tests/network_device_test.go index 15f2359396380..3a51c8746c023 100644 --- a/pkg/security/tests/network_device_test.go +++ b/pkg/security/tests/network_device_test.go @@ -24,6 +24,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/config" "github.com/DataDog/datadog-agent/pkg/security/ebpf/kernel" + sprobe "github.com/DataDog/datadog-agent/pkg/security/probe" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/secl/rules" "github.com/DataDog/datadog-agent/pkg/security/utils" @@ -211,10 +212,15 @@ func TestTCFilters(t *testing.T) { t.Error("Ingress tc classifier does not exist") } if !egressExists { - t.Fatalf("Egress tc classifier does not exist") + t.Fatal("Egress tc classifier does not exist") } - if err := test.probe.Manager.CleanupNetworkNamespace(nsid); err != nil { + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + t.Fatal("not supported") + } + + if err := p.Manager.CleanupNetworkNamespace(nsid); err != nil { t.Fatal(err) } diff --git a/pkg/security/tests/process_test.go b/pkg/security/tests/process_test.go index 46e312f9b21df..94fe815aba3cd 100644 --- a/pkg/security/tests/process_test.go +++ b/pkg/security/tests/process_test.go @@ -31,6 +31,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/config" "github.com/DataDog/datadog-agent/pkg/security/ebpf/kernel" + sprobe "github.com/DataDog/datadog-agent/pkg/security/probe" "github.com/DataDog/datadog-agent/pkg/security/probe/constantfetch" "github.com/avast/retry-go/v4" @@ -1323,6 +1324,11 @@ func TestProcessExecExit(t *testing.T) { } defer test.Close() + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + t.Skip("not supported") + } + var execPid uint32 err = test.GetProbeEvent(func() error { @@ -1367,8 +1373,7 @@ func TestProcessExecExit(t *testing.T) { // make sure that the process cache entry of the process was properly deleted from the cache err = retry.Do(func() error { - resolvers := test.probe.GetResolvers() - entry := resolvers.ProcessResolver.Get(execPid) + entry := p.Resolvers.ProcessResolver.Get(execPid) if entry != nil { return errors.New("the process cache entry was not deleted from the user space cache") } @@ -1992,6 +1997,11 @@ chmod 755 pyscript.py } defer testModule.Close() + p, ok := testModule.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + t.Skip("not supported") + } + for _, test := range tests { testModule.Run(t, test.name, func(t *testing.T, kind wrapperType, cmdFunc func(cmd string, args []string, envs []string) *exec.Cmd) { scriptLocation := fmt.Sprintf("/tmp/%s", test.scriptName) @@ -2009,7 +2019,7 @@ chmod 755 pyscript.py } t.Logf(string(output)) - offsets, _ := testModule.probe.GetOffsetConstants() + offsets, _ := p.GetOffsetConstants() t.Logf("%s: %+v\n", constantfetch.OffsetNameLinuxBinprmStructFile, offsets[constantfetch.OffsetNameLinuxBinprmStructFile]) return nil @@ -2035,6 +2045,11 @@ func TestProcessResolution(t *testing.T) { } defer test.Close() + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + t.Skip("not supported") + } + syscallTester, err := loadSyscallTester(t, test, "syscall_tester") if err != nil { t.Fatal(err) @@ -2087,7 +2102,7 @@ func TestProcessResolution(t *testing.T) { } inode := uint64(value.(int)) - resolvers := test.probe.GetResolvers() + resolver := p.Resolvers.ProcessResolver // compare only few fields as the hierarchy fields(pointers, etc) are modified by the resolution function calls equals := func(t *testing.T, entry1, entry2 *model.ProcessCacheEntry) { @@ -2105,12 +2120,12 @@ func TestProcessResolution(t *testing.T) { assert.Greater(t, time.Second, entry1.ForkTime.Sub(entry2.ForkTime).Abs()) } - cacheEntry := resolvers.ProcessResolver.ResolveFromCache(pid, pid, inode) + cacheEntry := resolver.ResolveFromCache(pid, pid, inode) if cacheEntry == nil { t.Errorf("not able to resolve the entry") } - mapsEntry := resolvers.ProcessResolver.ResolveFromKernelMaps(pid, pid, inode) + mapsEntry := resolver.ResolveFromKernelMaps(pid, pid, inode) if mapsEntry == nil { t.Errorf("not able to resolve the entry") } @@ -2119,7 +2134,7 @@ func TestProcessResolution(t *testing.T) { // This makes use of the cache and do not parse /proc // it still checks the ResolveFromProcfs returns the correct entry - procEntry := resolvers.ProcessResolver.ResolveFromProcfs(pid) + procEntry := resolver.ResolveFromProcfs(pid) if procEntry == nil { t.Fatalf("not able to resolve the entry") } diff --git a/pkg/security/tests/sbom_test.go b/pkg/security/tests/sbom_test.go index e964b885be5f6..d26f32aaeb97f 100644 --- a/pkg/security/tests/sbom_test.go +++ b/pkg/security/tests/sbom_test.go @@ -15,6 +15,7 @@ import ( "github.com/avast/retry-go/v4" + sprobe "github.com/DataDog/datadog-agent/pkg/security/probe" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/secl/rules" ) @@ -33,6 +34,11 @@ func TestSBOM(t *testing.T) { } defer test.Close() + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + t.Skip("not supported") + } + dockerWrapper, err := newDockerCmdWrapper(test.Root(), test.Root(), "ubuntu") if err != nil { t.Skip("Skipping sbom tests: Docker not available") @@ -43,7 +49,7 @@ func TestSBOM(t *testing.T) { dockerWrapper.Run(t, "package-rule", func(t *testing.T, kind wrapperType, cmdFunc func(bin string, args, env []string) *exec.Cmd) { test.WaitSignal(t, func() error { retry.Do(func() error { - sbom := test.probe.GetResolvers().SBOMResolver.GetWorkload(dockerWrapper.containerID) + sbom := p.Resolvers.SBOMResolver.GetWorkload(dockerWrapper.containerID) if sbom == nil { return fmt.Errorf("failed to find SBOM for '%s'", dockerWrapper.containerID) } diff --git a/pkg/security/tests/serializers_test.go b/pkg/security/tests/serializers_test.go index 77eeaf4fbfaff..e13ad56cd84db 100644 --- a/pkg/security/tests/serializers_test.go +++ b/pkg/security/tests/serializers_test.go @@ -61,7 +61,7 @@ func fetchRealisticEventSerializerInner(tb testing.TB) *serializers.EventSeriali assert.Equal(tb, "open", event.GetType(), "wrong event type") }) - return serializers.NewEventSerializer(workingEvent, test.probe.GetResolvers()) + return serializers.NewEventSerializer(workingEvent) } func BenchmarkSerializersEasyJson(b *testing.B) { diff --git a/pkg/security/tests/stress_test.go b/pkg/security/tests/stress_test.go index 705b5827a5b46..db6172d6900cb 100644 --- a/pkg/security/tests/stress_test.go +++ b/pkg/security/tests/stress_test.go @@ -17,6 +17,7 @@ import ( "testing" "time" + sprobe "github.com/DataDog/datadog-agent/pkg/security/probe" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/secl/rules" ) @@ -41,6 +42,11 @@ func stressOpen(t *testing.T, rule *rules.RuleDefinition, pathname string, size } defer test.Close() + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + t.Skip("not supported") + } + testFolder, _, err := test.Path(path.Dir(pathname)) if err != nil { t.Fatal(err) @@ -53,7 +59,8 @@ func stressOpen(t *testing.T, rule *rules.RuleDefinition, pathname string, size t.Fatal(err) } - eventStreamMonitor := test.probe.GetMonitors().GetEventStreamMonitor() + eventStreamMonitor := p.GetMonitors().GetEventStreamMonitor() + eventStreamMonitor.GetAndResetLostCount("events", -1) eventStreamMonitor.GetKernelLostCount("events", -1, model.MaxKernelEventType) @@ -183,6 +190,11 @@ func stressExec(t *testing.T, rule *rules.RuleDefinition, pathname string, execu } defer test.Close() + p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) + if !ok { + t.Skip("not supported") + } + testFolder, _, err := test.Path(path.Dir(pathname)) if err != nil { t.Fatal(err) @@ -195,7 +207,7 @@ func stressExec(t *testing.T, rule *rules.RuleDefinition, pathname string, execu t.Fatal(err) } - eventStreamMonitor := test.probe.GetMonitors().GetEventStreamMonitor() + eventStreamMonitor := p.GetMonitors().GetEventStreamMonitor() eventStreamMonitor.GetAndResetLostCount("events", -1) eventStreamMonitor.GetKernelLostCount("events", -1, model.MaxKernelEventType) diff --git a/tasks/security_agent.py b/tasks/security_agent.py index a573ac657b19b..5978a076baf4c 100644 --- a/tasks/security_agent.py +++ b/tasks/security_agent.py @@ -697,6 +697,11 @@ def generate_cws_proto(ctx): f"protoc -I. {plugin_opts} --go_out=paths=source_relative:. --go-vtproto_out=. --go-vtproto_opt=features=marshal+unmarshal+size --go-grpc_out=paths=source_relative:. pkg/security/proto/api/api.proto" ) + # EBPFLESS + ctx.run( + f"protoc -I. {plugin_opts} --go_out=paths=source_relative:. --go-vtproto_out=. --go-vtproto_opt=features=marshal+unmarshal+size --go-grpc_out=paths=source_relative:. pkg/security/proto/ebpfless/service.proto" + ) + for path in glob.glob("pkg/security/**/*.pb.go", recursive=True): print(f"replacing protoc version in {path}") with open(path) as f: From 15a05980eb513b1a6d81fdf069c010d93a09f591 Mon Sep 17 00:00:00 2001 From: Kevin Fairise <132568982+KevinFairise2@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:22:53 +0100 Subject: [PATCH 25/87] Remove flaky debian 9 (#21114) Remove flaky debian 9 --- test/new-e2e/tests/agent-platform/platforms/platforms.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/new-e2e/tests/agent-platform/platforms/platforms.json b/test/new-e2e/tests/agent-platform/platforms/platforms.json index 655c8b767a1c4..7f905398eef76 100644 --- a/test/new-e2e/tests/agent-platform/platforms/platforms.json +++ b/test/new-e2e/tests/agent-platform/platforms/platforms.json @@ -1,7 +1,7 @@ { "debian": { "x86_64": { - "debian-9": "ami-06441ffb13c981873", + "debian-9": "ami-099d228beefd189f5", "debian-10": "ami-041540a5c191757a0", "debian-11": "ami-09e24b0cfe072ecef", "debian-12": "ami-06db4d78cb1d3bbf9" From 448d3506c2468e59dc2143aac495c341ef55f990 Mon Sep 17 00:00:00 2001 From: Usama Saqib Date: Mon, 27 Nov 2023 17:35:31 +0100 Subject: [PATCH 26/87] Move windows kitchen tests to 'functional_test' stage (#21001) * move windows tests back to 'functional_test' stage * move shared job to include file * remove shared job * move stage definitions out of common job * remove stage * add WKIT team as the owner * remove ebpf-platform as co-owner of windows tests job * add CODEOWNERS file for functional_test/common.yml --- .github/CODEOWNERS | 2 ++ .gitlab-ci.yml | 2 ++ .gitlab/functional_test.yml | 1 + .gitlab/functional_test/common.yml | 18 ++++++++++ .../functional_test/system_probe_windows.yml | 29 +++++++++++++++ .../functional_test_sysprobe/system_probe.yml | 36 ++----------------- tasks/unit-tests/testdata/fake_gitlab-ci.yml | 1 + 7 files changed, 55 insertions(+), 34 deletions(-) create mode 100644 .gitlab/functional_test/common.yml create mode 100644 .gitlab/functional_test/system_probe_windows.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2761c2fc79b68..dbd8d75bb04be 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -83,6 +83,8 @@ /.gitlab/functional_test/security_agent.yml @DataDog/agent-security @DataDog/agent-platform /.gitlab/functional_test/serverless.yml @DataDog/serverless @DataDog/agent-platform /.gitlab/functional_test_cleanup.yml @DataDog/agent-security @DataDog/ebpf-platform @DataDog/agent-platform +/.gitlab/functional_test/system_probe_windows.yml @DataDog/agent-platform @DataDog/windows-kernel-integrations +/.gitlab/functional_test/common.yml @DataDog/agent-platform @DataDog/windows-kernel-integrations @DataDog/ebpf-platform /.gitlab/functional_test_sysprobe/system_probe.yml @DataDog/ebpf-platform @DataDog/agent-platform /.gitlab/integration_test/windows.yml @DataDog/agent-platform @DataDog/windows-agent diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index de6c6ae8e0ef9..f53edc6eaf0dc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -45,6 +45,7 @@ include: - /.gitlab/notify.yml - /.gitlab/kitchen_common/cleanup.yml - /.gitlab/kitchen_common/testing.yml + - /.gitlab/functional_test/common.yml - /.gitlab/benchmarks/benchmarks.yml - /.gitlab/benchmarks/macrobenchmarks.yml - /.gitlab/rc_kubernetes_deploy.yml @@ -952,6 +953,7 @@ workflow: - test/kitchen/site-cookbooks/dd-system-probe-check/**/* - test/kitchen/test/integration/system-probe-test/**/* - test/kitchen/test/integration/win-sysprobe-test/**/* + - .gitlab/functional_test/system_probe_windows.yml - .gitlab/functional_test_sysprobe/system_probe.yml - .gitlab/kernel_version_testing/system_probe.yml - test/new-e2e/system-probe/**/* diff --git a/.gitlab/functional_test.yml b/.gitlab/functional_test.yml index f83155f796bf9..0bfe548bc118d 100644 --- a/.gitlab/functional_test.yml +++ b/.gitlab/functional_test.yml @@ -7,5 +7,6 @@ include: - /.gitlab/functional_test/serverless.yml - /.gitlab/functional_test/regression_detector.yml - /.gitlab/functional_test/workload_checks.yml + - /.gitlab/functional_test/system_probe_windows.yml - /.gitlab/kernel_version_testing/system_probe.yml - /.gitlab/functional_test_sysprobe/system_probe.yml diff --git a/.gitlab/functional_test/common.yml b/.gitlab/functional_test/common.yml new file mode 100644 index 0000000000000..2d216eb39e250 --- /dev/null +++ b/.gitlab/functional_test/common.yml @@ -0,0 +1,18 @@ +--- +# FIXME: our current Gitlab version doesn't support importing a file more than once +# For now, the workaround is to include "common" files once in the top-level .gitlab-ci.yml file +# See: https://gitlab.com/gitlab-org/gitlab/-/issues/28987 +# include: +# - /.gitlab/kitchen_common/testing.yml + +.kitchen_test_system_probe: + extends: + - .kitchen_common + - .kitchen_datadog_agent_flavor + rules: + !reference [.on_system_probe_changes_or_manual] + timeout: 3h + variables: + AGENT_MAJOR_VERSION: 7 + DD_PIPELINE_ID: $CI_PIPELINE_ID-fnct + CHEF_VERSION: 14.15.6 diff --git a/.gitlab/functional_test/system_probe_windows.yml b/.gitlab/functional_test/system_probe_windows.yml new file mode 100644 index 0000000000000..73e3c5a209221 --- /dev/null +++ b/.gitlab/functional_test/system_probe_windows.yml @@ -0,0 +1,29 @@ +--- +# FIXME: our current Gitlab version doesn't support importing a file more than once +# For now, the workaround is to include "common" files once in the top-level .gitlab-ci.yml file +# See: https://gitlab.com/gitlab-org/gitlab/-/issues/28987 +# include: +# - /.gitlab/kitchen_common/testing.yml +# - /.gitlab/functional_test/common.yml + +kitchen_test_system_probe_windows_x64: + extends: + - .kitchen_agent_a7 + - .kitchen_os_windows + - .kitchen_test_system_probe + - .kitchen_azure_x64 + - .kitchen_azure_location_north_central_us + stage: functional_test + needs: [ "tests_windows_sysprobe_x64" ] + variables: + KITCHEN_ARCH: x86_64 + KITCHEN_OSVERS: "win2016" + CHEF_VERSION: 14.12.9 # newer versions error out during kitchen setup of azure VM + before_script: + - export WINDOWS_DDNPM_DRIVER=$(inv release.get-release-json-value "$RELEASE_VERSION_7::WINDOWS_DDNPM_DRIVER") + - export WINDOWS_DDNPM_VERSION=$(inv release.get-release-json-value "$RELEASE_VERSION_7::WINDOWS_DDNPM_VERSION") + - export WINDOWS_DDNPM_SHASUM=$(inv release.get-release-json-value "$RELEASE_VERSION_7::WINDOWS_DDNPM_SHASUM") + - pushd $DD_AGENT_TESTING_DIR + - tasks/kitchen_setup.sh + script: + - tasks/run-test-kitchen.sh windows-sysprobe-test $AGENT_MAJOR_VERSION diff --git a/.gitlab/functional_test_sysprobe/system_probe.yml b/.gitlab/functional_test_sysprobe/system_probe.yml index c621c6d9d6206..a3665e23db4e5 100644 --- a/.gitlab/functional_test_sysprobe/system_probe.yml +++ b/.gitlab/functional_test_sysprobe/system_probe.yml @@ -4,23 +4,12 @@ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/28987 # include: # - /.gitlab/kitchen_common/testing.yml - -.kitchen_test_system_probe: - extends: - - .kitchen_common - - .kitchen_datadog_agent_flavor - rules: - !reference [.on_system_probe_changes_or_manual] - stage: functional_test_sysprobe - timeout: 3h - variables: - AGENT_MAJOR_VERSION: 7 - DD_PIPELINE_ID: $CI_PIPELINE_ID-fnct - CHEF_VERSION: 14.15.6 +# - /.gitlab/functional_test/common.yml .kitchen_test_system_probe_linux: extends: - .kitchen_test_system_probe + stage: functional_test_sysprobe before_script: - echo "CI_JOB_URL=${CI_JOB_URL}" >> $DD_AGENT_TESTING_DIR/site-cookbooks/dd-system-probe-check/files/job_env.txt - echo "CI_JOB_ID=${CI_JOB_ID}" >> $DD_AGENT_TESTING_DIR/site-cookbooks/dd-system-probe-check/files/job_env.txt @@ -180,24 +169,3 @@ kitchen_test_system_probe_linux_arm64: KITCHEN_OSVERS: "ubuntu-23-04" - KITCHEN_PLATFORM: "ubuntu" KITCHEN_OSVERS: "ubuntu-23-10" - -kitchen_test_system_probe_windows_x64: - extends: - - .kitchen_agent_a7 - - .kitchen_os_windows - - .kitchen_test_system_probe - - .kitchen_azure_x64 - - .kitchen_azure_location_north_central_us - needs: [ "tests_windows_sysprobe_x64" ] - variables: - KITCHEN_ARCH: x86_64 - KITCHEN_OSVERS: "win2016" - CHEF_VERSION: 14.12.9 # newer versions error out during kitchen setup of azure VM - before_script: - - export WINDOWS_DDNPM_DRIVER=$(inv release.get-release-json-value "$RELEASE_VERSION_7::WINDOWS_DDNPM_DRIVER") - - export WINDOWS_DDNPM_VERSION=$(inv release.get-release-json-value "$RELEASE_VERSION_7::WINDOWS_DDNPM_VERSION") - - export WINDOWS_DDNPM_SHASUM=$(inv release.get-release-json-value "$RELEASE_VERSION_7::WINDOWS_DDNPM_SHASUM") - - pushd $DD_AGENT_TESTING_DIR - - tasks/kitchen_setup.sh - script: - - tasks/run-test-kitchen.sh windows-sysprobe-test $AGENT_MAJOR_VERSION diff --git a/tasks/unit-tests/testdata/fake_gitlab-ci.yml b/tasks/unit-tests/testdata/fake_gitlab-ci.yml index 44a2db553df03..2b54fe1966c6b 100644 --- a/tasks/unit-tests/testdata/fake_gitlab-ci.yml +++ b/tasks/unit-tests/testdata/fake_gitlab-ci.yml @@ -906,6 +906,7 @@ workflow: - test/kitchen/site-cookbooks/dd-system-probe-check/**/* - test/kitchen/test/integration/system-probe-test/**/* - test/kitchen/test/integration/win-sysprobe-test/**/* + - .gitlab/functional_test/system_probe_windows.yml - .gitlab/functional_test_sysprobe/system_probe.yml - .gitlab/kernel_version_testing/system_probe.yml - test/new-e2e/system-probe/**/* From e912afad91edcba56692da15d3e4ba96580799e4 Mon Sep 17 00:00:00 2001 From: Julien Lebot Date: Mon, 27 Nov 2023 17:47:29 +0100 Subject: [PATCH 27/87] Export log attribute for the windows registry integration (#21117) --- .../integration_logs_registry_delegate.go | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/comp/checks/winregistry/impl/integration_logs_registry_delegate.go b/comp/checks/winregistry/impl/integration_logs_registry_delegate.go index 79070f092e3d1..ab0e868dc28c0 100644 --- a/comp/checks/winregistry/impl/integration_logs_registry_delegate.go +++ b/comp/checks/winregistry/impl/integration_logs_registry_delegate.go @@ -30,16 +30,16 @@ const ( // logEvent struct defines the extra attributes that are sent along with the log message type logEvent struct { - keyPath string `yaml:"key_path"` - eventType string `yaml:"event_type"` + KeyPath string `yaml:"key_path"` + EventType string `yaml:"event_type"` // process_id, process_name and thread_id will be coming in a future version. } type valueChangedLogEvent struct { logEvent - oldValue interface{} `yaml:"old_value"` - newValue interface{} `yaml:"new_value"` + OldValue interface{} `yaml:"old_value"` + NewValue interface{} `yaml:"new_value"` } func (i *integrationLogsRegistryDelegate) sendLog(status string, payload message.BasicStructuredContent) { @@ -83,10 +83,10 @@ func (i *integrationLogsRegistryDelegate) processValue(valueName, originalVal st log = fmt.Sprintf("value %s = '%v'", keyName, val) } i.sendLog("info", getPayload[valueChangedLogEvent](log, func(e *valueChangedLogEvent) { - e.keyPath = keyName - e.eventType = eventType - e.oldValue = nil - e.newValue = val + e.KeyPath = keyName + e.EventType = eventType + e.OldValue = nil + e.NewValue = val })) i.valueMap[keyName] = val } else if cachedVal != val { @@ -97,10 +97,10 @@ func (i *integrationLogsRegistryDelegate) processValue(valueName, originalVal st log = fmt.Sprintf("value %s changed from '%v' to '%v'", keyName, cachedVal, val) } i.sendLog("info", getPayload[valueChangedLogEvent](log, func(e *valueChangedLogEvent) { - e.keyPath = keyName - e.eventType = eventType - e.oldValue = cachedVal - e.newValue = val + e.KeyPath = keyName + e.EventType = eventType + e.OldValue = cachedVal + e.NewValue = val })) i.valueMap[keyName] = val } @@ -123,10 +123,10 @@ func (i *integrationLogsRegistryDelegate) onMissing(valueName string, regKeyCfg cachedVal, ok := i.valueMap[keyName] if ok { i.sendLog("info", getPayload[valueChangedLogEvent](fmt.Sprintf("value %s ('%v') was deleted", keyName, cachedVal), func(e *valueChangedLogEvent) { - e.keyPath = keyName - e.eventType = keyDeleted - e.oldValue = cachedVal - e.newValue = nil + e.KeyPath = keyName + e.EventType = keyDeleted + e.OldValue = cachedVal + e.NewValue = nil })) delete(i.valueMap, keyName) } From 15265cf3ac208c4f4e026d3e00df02f21f8aa3de Mon Sep 17 00:00:00 2001 From: Sylvain Afchain Date: Mon, 27 Nov 2023 18:11:04 +0100 Subject: [PATCH 28/87] [CWS] remove useless selinux schema check (#21121) --- .../tests/schemas/selinux.schema.json | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/pkg/security/tests/schemas/selinux.schema.json b/pkg/security/tests/schemas/selinux.schema.json index e2d72e3b2d79e..5cff42b712ef4 100644 --- a/pkg/security/tests/schemas/selinux.schema.json +++ b/pkg/security/tests/schemas/selinux.schema.json @@ -96,42 +96,6 @@ ] } }, - "allOf": [ - { - "if": { - "properties": { - "file": { - "type": "object", - "required": [ - "container_path" - ] - } - } - }, - "then": { - "required": [ - "container" - ] - } - }, - { - "if": { - "required": [ - "container" - ] - }, - "then": { - "properties": { - "file": { - "type": "object", - "required": [ - "container_path" - ] - } - } - } - } - ], "required": [ "selinux" ] From 98de06cced847aec4e08c7b9bc0f0b6589d2213e Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Mon, 27 Nov 2023 18:50:13 +0100 Subject: [PATCH 29/87] CWS: skip selinux test before creating a module if needed (#21123) --- pkg/security/tests/selinux_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/security/tests/selinux_test.go b/pkg/security/tests/selinux_test.go index 5780a34797932..ae2cabcc4217d 100644 --- a/pkg/security/tests/selinux_test.go +++ b/pkg/security/tests/selinux_test.go @@ -150,6 +150,10 @@ func TestSELinux(t *testing.T) { } func TestSELinuxCommitBools(t *testing.T) { + if !isSELinuxEnabled() { + t.Skipf("SELinux is not available, skipping tests") + } + ruleset := []*rules.RuleDefinition{ { ID: "test_selinux_commit_bools", @@ -163,10 +167,6 @@ func TestSELinuxCommitBools(t *testing.T) { } defer test.Close() - if !isSELinuxEnabled() { - t.Skipf("SELinux is not available, skipping tests") - } - savedBoolValue, err := getBoolValue(TestBoolName) if err != nil { t.Fatalf("failed to save bool state: %v", err) From 3365cbff871ca1ab9aa08fa58e1544649013b717 Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Mon, 27 Nov 2023 18:51:06 +0100 Subject: [PATCH 30/87] CWS: switch ringbuffer to `RecordGetter/RecordHandler` model (#21122) * CWS: switch ringbuffer to `RecordGetter/RecordHandler` model * fix lint --- .../eventstream/ringbuffer/ringbuffer.go | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/pkg/security/probe/eventstream/ringbuffer/ringbuffer.go b/pkg/security/probe/eventstream/ringbuffer/ringbuffer.go index 4b8117d385923..4748691f3e7a6 100644 --- a/pkg/security/probe/eventstream/ringbuffer/ringbuffer.go +++ b/pkg/security/probe/eventstream/ringbuffer/ringbuffer.go @@ -13,6 +13,7 @@ import ( "sync" manager "github.com/DataDog/ebpf-manager" + "github.com/cilium/ebpf/ringbuf" "github.com/DataDog/datadog-agent/pkg/security/probe/config" "github.com/DataDog/datadog-agent/pkg/security/probe/eventstream" @@ -23,6 +24,7 @@ import ( type RingBuffer struct { ringBuffer *manager.RingBuffer handler func(int, []byte) + recordPool *sync.Pool } // Init the ring buffer @@ -33,7 +35,10 @@ func (rb *RingBuffer) Init(mgr *manager.Manager, config *config.Config) error { } rb.ringBuffer.RingBufferOptions = manager.RingBufferOptions{ - DataHandler: rb.handleEvent, + RecordGetter: func() *ringbuf.Record { + return rb.recordPool.Get().(*ringbuf.Record) + }, + RecordHandler: rb.handleEvent, } if config.EventStreamBufferSize != 0 { @@ -51,8 +56,9 @@ func (rb *RingBuffer) Start(wg *sync.WaitGroup) error { // SetMonitor set the monitor func (rb *RingBuffer) SetMonitor(counter eventstream.LostEventCounter) {} -func (rb *RingBuffer) handleEvent(CPU int, data []byte, ringBuffer *manager.RingBuffer, manager *manager.Manager) { - rb.handler(CPU, data) +func (rb *RingBuffer) handleEvent(record *ringbuf.Record, _ *manager.RingBuffer, _ *manager.Manager) { + rb.handler(0, record.RawSample) + rb.recordPool.Put(record) } // Pause the event stream. Do nothing when using ring buffer @@ -67,7 +73,14 @@ func (rb *RingBuffer) Resume() error { // New returns a new ring buffer based event stream. func New(handler func(int, []byte)) *RingBuffer { + recordPool := &sync.Pool{ + New: func() interface{} { + return new(ringbuf.Record) + }, + } + return &RingBuffer{ - handler: handler, + recordPool: recordPool, + handler: handler, } } From c3f441ca77711ebc72a4a1fb1374c8569ce4ed6b Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Mon, 27 Nov 2023 18:51:34 +0100 Subject: [PATCH 31/87] [CWS] usergroup test cleanup (#21116) * usergroup test: skip if docker sooner * move distro part into own `t.Run` group * fix centos docker image url --- pkg/security/tests/cmdwrapper.go | 2 +- pkg/security/tests/usergroup_test.go | 51 +++++++++++++++------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/pkg/security/tests/cmdwrapper.go b/pkg/security/tests/cmdwrapper.go index 2ad90d35d0909..434fe7b2457fd 100644 --- a/pkg/security/tests/cmdwrapper.go +++ b/pkg/security/tests/cmdwrapper.go @@ -36,7 +36,7 @@ var dockerImageLibrary = map[string][]string{ }, "centos": { "centos:7", - "public.ecr.aws/centos/centos:7", + "public.ecr.aws/docker/library/centos:7", }, "alpine": { "alpine", diff --git a/pkg/security/tests/usergroup_test.go b/pkg/security/tests/usergroup_test.go index 19336177f9081..c52d43a8a62ba 100644 --- a/pkg/security/tests/usergroup_test.go +++ b/pkg/security/tests/usergroup_test.go @@ -17,6 +17,10 @@ import ( ) func TestUserGroup(t *testing.T) { + if testEnvironment == DockerEnvironment { + t.Skip("Skip test spawning docker containers on docker") + } + ruleDefs := []*rules.RuleDefinition{ { ID: "test_rule_user", @@ -118,31 +122,32 @@ func TestUserGroup(t *testing.T) { defer test.Close() for _, distroTest := range distroTests { - dockerWrapper, err := newDockerCmdWrapper(test.Root(), test.Root(), distroTest.name) - if err != nil { - t.Skipf("Skipping user group tests: Docker not available: %s", err) - return - } + t.Run(distroTest.name, func(t *testing.T) { + dockerWrapper, err := newDockerCmdWrapper(test.Root(), test.Root(), distroTest.name) + if err != nil { + t.Fatal(err) + } - if _, err := dockerWrapper.start(); err != nil { - t.Fatal(err) - } - defer dockerWrapper.stop() + if _, err := dockerWrapper.start(); err != nil { + t.Fatal(err) + } + defer dockerWrapper.stop() - for _, testCommand := range distroTest.testCommands { - i := 0 - dockerWrapper.RunTest(t, distroTest.name+"-"+testCommand.name, func(t *testing.T, kind wrapperType, cmdFunc func(bin string, args, env []string) *exec.Cmd) { - test.WaitSignals(t, func() error { - return cmdFunc(testCommand.cmd[0], testCommand.cmd[1:], nil).Run() - }, func(event *model.Event, rule *rules.Rule) error { - assertTriggeredRule(t, rule, testCommand.rules[i]) - i++ - if i < len(testCommand.rules) { - return errSkipEvent - } - return nil + for _, testCommand := range distroTest.testCommands { + i := 0 + dockerWrapper.RunTest(t, testCommand.name, func(t *testing.T, kind wrapperType, cmdFunc func(bin string, args, env []string) *exec.Cmd) { + test.WaitSignals(t, func() error { + return cmdFunc(testCommand.cmd[0], testCommand.cmd[1:], nil).Run() + }, func(event *model.Event, rule *rules.Rule) error { + assertTriggeredRule(t, rule, testCommand.rules[i]) + i++ + if i < len(testCommand.rules) { + return errSkipEvent + } + return nil + }) }) - }) - } + } + }) } } From 4e0bd6e2c28323a2947262110ee3f6e611b23f2d Mon Sep 17 00:00:00 2001 From: Nicolas Schweitzer Date: Mon, 27 Nov 2023 19:16:40 +0100 Subject: [PATCH 32/87] Add new channels for slapr update (#21118) Add new channels for slapr update --- .github/workflows/slapr.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/slapr.yml b/.github/workflows/slapr.yml index f04b355b08936..45feb5dbf325a 100644 --- a/.github/workflows/slapr.yml +++ b/.github/workflows/slapr.yml @@ -14,6 +14,10 @@ jobs: channel_variables: - SLACK_AGENT_PLATFORM_ID - SLACK_AGENT_PLATFORM_REVIEWS_ID + - SLACK_OPENTELEMETRY_ID + - SLACK_AGENT_METRICS_LOGS_ID + - SLACK_CONTAINER_INTEGRATIONS_ID + - SLACK_AGENT_SHARED_COMPONENTS_ID steps: - uses: DataDog/slapr@master env: @@ -23,6 +27,6 @@ jobs: SLAPR_BOT_USER_ID: "${{ secrets.SLAPR_BOT_USER_ID }}" SLAPR_EMOJI_REVIEW_STARTED: "review_started" SLAPR_EMOJI_APPROVED: "gh-approved" - SLAPR_EMOJI_CHANGES_REQUESTED: "changes_requested" + SLAPR_EMOJI_CHANGES_REQUESTED: "request-changes" SLAPR_EMOJI_MERGED: "merged" SLAPR_EMOJI_CLOSED: "closed" From a3b8e6d0b8e8b24a3750050ca9f6be9d72a95bd8 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Mon, 27 Nov 2023 14:17:22 -0500 Subject: [PATCH 33/87] Consolidates on ipc_address and deprecates cmd_host (#20976) --- docs/dev/agent_api.md | 2 +- pkg/config/config.go | 3 +-- pkg/jmxfetch/jmxfetch.go | 5 ++++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/dev/agent_api.md b/docs/dev/agent_api.md index f55ffaaf6e416..e43ba211543a5 100644 --- a/docs/dev/agent_api.md +++ b/docs/dev/agent_api.md @@ -5,7 +5,7 @@ groups: 2. **Telemetry**: These expose some internal telemetry that is useful for profiling and debugging. ## Control API -This API is accessible via HTTPS only and listens by default on the `localhost` interface on port `5001`. The listening interface and port can be configured using the `cmd_host` and `cmd_port` config options. +This API is accessible via HTTPS only and listens by default on the `localhost` interface on port `5001`. The listening interface and port can be configured using the `ipc_address` and `cmd_port` config options. ### Authentication To avoid unprivileged users accessing the Agent control API, authentication is required and based on a generated token. diff --git a/pkg/config/config.go b/pkg/config/config.go index c35d180acd707..48f3bf34e3b19 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -245,7 +245,7 @@ func InitConfig(config Config) { config.BindEnvAndSetDefault("syslog_pem", "") config.BindEnvAndSetDefault("syslog_key", "") config.BindEnvAndSetDefault("syslog_tls_verify", true) - config.BindEnvAndSetDefault("cmd_host", "localhost") + config.BindEnvAndSetDefault("ipc_address", "localhost") config.BindEnvAndSetDefault("cmd_port", 5001) config.BindEnvAndSetDefault("default_integration_http_timeout", 9) config.BindEnvAndSetDefault("integration_tracing", false) @@ -257,7 +257,6 @@ func InitConfig(config Config) { config.BindEnvAndSetDefault("check_runners", int64(4)) config.BindEnvAndSetDefault("auth_token_file_path", "") config.BindEnv("bind_host") - config.BindEnvAndSetDefault("ipc_address", "localhost") config.BindEnvAndSetDefault("health_port", int64(0)) config.BindEnvAndSetDefault("disable_py3_validation", false) config.BindEnvAndSetDefault("python_version", DefaultPython) diff --git a/pkg/jmxfetch/jmxfetch.go b/pkg/jmxfetch/jmxfetch.go index 8949f4d10f812..798831882d7c2 100644 --- a/pkg/jmxfetch/jmxfetch.go +++ b/pkg/jmxfetch/jmxfetch.go @@ -268,7 +268,10 @@ func (j *JMXFetch) Start(manage bool) error { jmxLogLevel = "INFO" } - ipcHost := config.Datadog.GetString("cmd_host") + ipcHost, err := config.GetIPCAddress() + if err != nil { + return err + } ipcPort := config.Datadog.GetInt("cmd_port") if j.IPCHost != "" { ipcHost = j.IPCHost From 0ef4a270cf81c44d06004098a9e234ac156045b6 Mon Sep 17 00:00:00 2001 From: Nenad Noveljic <18366081+nenadnoveljic@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:45:54 +0100 Subject: [PATCH 34/87] [oracle] Activity sampling optimization (DBMON-2996) (#21057) * Tuned the activity query * relnote --- .../corechecks/oracle-dbm/activity_queries.go | 2 +- ...ctivity-samples-optimization-70b4b39291a6cd11.yaml | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/oracle-activity-samples-optimization-70b4b39291a6cd11.yaml diff --git a/pkg/collector/corechecks/oracle-dbm/activity_queries.go b/pkg/collector/corechecks/oracle-dbm/activity_queries.go index 9b81126715b62..4a3008a4f89ed 100644 --- a/pkg/collector/corechecks/oracle-dbm/activity_queries.go +++ b/pkg/collector/corechecks/oracle-dbm/activity_queries.go @@ -150,7 +150,7 @@ WHERE ) AND status = 'ACTIVE'` -const activityQueryDirect = `SELECT /* DD_ACTIVITY_SAMPLING */ +const activityQueryDirect = `SELECT /*+ push_pred(sq) push_pred(sq_prev) */ /* DD_ACTIVITY_SAMPLING */ s.sid, s.serial#, s.username, diff --git a/releasenotes/notes/oracle-activity-samples-optimization-70b4b39291a6cd11.yaml b/releasenotes/notes/oracle-activity-samples-optimization-70b4b39291a6cd11.yaml new file mode 100644 index 0000000000000..a1c1c920e8131 --- /dev/null +++ b/releasenotes/notes/oracle-activity-samples-optimization-70b4b39291a6cd11.yaml @@ -0,0 +1,11 @@ +# Each section from every release note are combined when the +# CHANGELOG.rst is rendered. So the text needs to be worded so that +# it does not depend on any information only available in another +# section. This may mean repeating some details, but each section +# must be readable independently of the other. +# +# Each section note must be formatted as reStructuredText. +--- +enhancements: + - | + Improved performance of the activity sampling query on RDS and Oracle Cloud databases. From 10197ab7325014b1a4bb33b69d6dfe11faf3aea1 Mon Sep 17 00:00:00 2001 From: Nenad Noveljic <18366081+nenadnoveljic@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:46:22 +0100 Subject: [PATCH 35/87] Bug fix for missing tags (#21083) * Bug fix for missing tags * relnote --- .../corechecks/oracle-dbm/init_test.go | 26 ++++++++++++++ pkg/collector/corechecks/oracle-dbm/oracle.go | 4 +++ .../corechecks/oracle-dbm/sysmetrics_test.go | 2 +- .../corechecks/oracle-dbm/testutil.go | 35 +++++++++++++++++++ .../oracle-missing-tags-8e25d3d3b5041354.yaml | 11 ++++++ 5 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 pkg/collector/corechecks/oracle-dbm/init_test.go create mode 100644 releasenotes/notes/oracle-missing-tags-8e25d3d3b5041354.yaml diff --git a/pkg/collector/corechecks/oracle-dbm/init_test.go b/pkg/collector/corechecks/oracle-dbm/init_test.go new file mode 100644 index 0000000000000..b3bca56565216 --- /dev/null +++ b/pkg/collector/corechecks/oracle-dbm/init_test.go @@ -0,0 +1,26 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build oracle_test + +package oracle + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTags(t *testing.T) { + c, _ := newRealCheck(t, `tags: + - foo1:bar1 + - foo2:bar2`) + err := c.Run() + require.NoError(t, err) + assert.True(t, c.initialized, "Check not initialized") + assert.Contains(t, c.tags, dbmsTag, "Static tag not merged") + assert.Contains(t, c.tags, "foo1:bar1", "Config tag not in tags") +} diff --git a/pkg/collector/corechecks/oracle-dbm/oracle.go b/pkg/collector/corechecks/oracle-dbm/oracle.go index bc2f6faf677a8..e39f3fc884440 100644 --- a/pkg/collector/corechecks/oracle-dbm/oracle.go +++ b/pkg/collector/corechecks/oracle-dbm/oracle.go @@ -316,6 +316,7 @@ func (c *Check) Configure(senderManager sender.SenderManager, integrationConfigD c.checkInterval = float64(c.config.InitConfig.MinCollectionInterval) tags := make([]string, len(c.config.Tags)) + copy(tags, c.config.Tags) tags = append(tags, fmt.Sprintf("dbms:%s", common.IntegrationName), fmt.Sprintf("ddagentversion:%s", c.agentVersion)) tags = append(tags, fmt.Sprintf("dbm:%t", c.dbmEnabled)) @@ -331,7 +332,10 @@ func (c *Check) Configure(senderManager sender.SenderManager, integrationConfigD if c.config.ServiceName != "" { tags = append(tags, fmt.Sprintf("service:%s", c.config.ServiceName)) } + c.configTags = make([]string, len(tags)) copy(c.configTags, tags) + c.tags = make([]string, len(tags)) + copy(c.tags, tags) c.logPrompt = config.GetLogPrompt(c.config.InstanceConfig) diff --git a/pkg/collector/corechecks/oracle-dbm/sysmetrics_test.go b/pkg/collector/corechecks/oracle-dbm/sysmetrics_test.go index e4de2c1549cc0..60470982d7d35 100644 --- a/pkg/collector/corechecks/oracle-dbm/sysmetrics_test.go +++ b/pkg/collector/corechecks/oracle-dbm/sysmetrics_test.go @@ -16,5 +16,5 @@ import ( func TestSysmetrics(t *testing.T) { n, err := chk.sysMetrics() assert.NoError(t, err, "failed to run sys metrics") - assert.Equal(t, int64(144), n) + assert.Equal(t, int64(92), n) } diff --git a/pkg/collector/corechecks/oracle-dbm/testutil.go b/pkg/collector/corechecks/oracle-dbm/testutil.go index 14feaa7da7e1f..c39b0e27d0cf1 100644 --- a/pkg/collector/corechecks/oracle-dbm/testutil.go +++ b/pkg/collector/corechecks/oracle-dbm/testutil.go @@ -11,8 +11,10 @@ import ( "fmt" "testing" + "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" "github.com/DataDog/datadog-agent/pkg/aggregator/sender" "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -37,3 +39,36 @@ var PASSWORD = "datadog" var SERVICE_NAME = "XE" var TNS_ALIAS = "XE" var TNS_ADMIN = "/Users/nenad.noveljic/go/src/github.com/DataDog/datadog-agent/pkg/collector/corechecks/oracle-dbm/testutil/etc/netadmin" + +var dbmsTag = "dbms:oracle" + +func newRealCheck(t *testing.T, options string) (Check, *mocksender.MockSender) { + c := Check{} + config := fmt.Sprintf(` +server: %s +port: %d +username: %s +password: %s +service_name: %s +`, HOST, PORT, USER, PASSWORD, SERVICE_NAME) + if options != "" { + config = fmt.Sprintf(`%s +%s`, config, options) + } + rawInstanceConfig := []byte(config) + senderManager := mocksender.CreateDefaultDemultiplexer() + err := c.Configure(senderManager, integration.FakeConfigHash, rawInstanceConfig, []byte(``), "oracle_test") + require.NoError(t, err) + + sender := mocksender.NewMockSenderWithSenderManager(c.ID(), senderManager) + sender.SetupAcceptAll() + assert.Equal(t, c.config.InstanceConfig.Server, HOST) + assert.Equal(t, c.config.InstanceConfig.Port, PORT) + assert.Equal(t, c.config.InstanceConfig.Username, USER) + assert.Equal(t, c.config.InstanceConfig.Password, PASSWORD) + assert.Equal(t, c.config.InstanceConfig.ServiceName, SERVICE_NAME) + + assert.Contains(t, c.configTags, dbmsTag, "c.configTags doesn't contain static tags") + + return c, sender +} diff --git a/releasenotes/notes/oracle-missing-tags-8e25d3d3b5041354.yaml b/releasenotes/notes/oracle-missing-tags-8e25d3d3b5041354.yaml new file mode 100644 index 0000000000000..fff3a24bbf6b3 --- /dev/null +++ b/releasenotes/notes/oracle-missing-tags-8e25d3d3b5041354.yaml @@ -0,0 +1,11 @@ +# Each section from every release note are combined when the +# CHANGELOG.rst is rendered. So the text needs to be worded so that +# it does not depend on any information only available in another +# section. This may mean repeating some details, but each section +# must be readable independently of the other. +# +# Each section note must be formatted as reStructuredText. +--- +fixes: + - | + Bug fix for missing tags. From 04ff9923219f52fd5a19673ad20c71ffd3ab70f0 Mon Sep 17 00:00:00 2001 From: Srdjan Grubor Date: Mon, 27 Nov 2023 14:54:19 -0600 Subject: [PATCH 36/87] [ddqa] Update excluded members for @DataDog/#agent-shared-components team (#21125) The membership of the team has been updated recently so the QA config is updated accordingly. --- .ddqa/config.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.ddqa/config.toml b/.ddqa/config.toml index 62996a97c319b..99a5ea254056f 100644 --- a/.ddqa/config.toml +++ b/.ddqa/config.toml @@ -30,7 +30,10 @@ jira_statuses = [ ] github_team = "agent-shared-components" github_labels = ["team/agent-shared-components"] -exclude_members = ["olivielpeau", "sgnn7"] +exclude_members = [ + "sgnn7", + "truthbk", +] [teams."Agent Platform"] jira_project = "APL" From 63f5907914956a68b858e37c264085f076e5896f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9na=C3=AFc=20Huard?= Date: Mon, 27 Nov 2023 22:04:03 +0100 Subject: [PATCH 37/87] Bump `test-infra-definitions` (#21124) Bump `test-infra-definitions` --- .gitlab-ci.yml | 2 +- test/new-e2e/go.mod | 6 ++++-- test/new-e2e/go.sum | 10 ++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f53edc6eaf0dc..e814cbe8fe239 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -177,7 +177,7 @@ variables: # To use images from test-infra-definitions dev branches, set the SUFFIX variable to -dev # and check the job creating the image to make sure you have the right SHA prefix TEST_INFRA_DEFINITIONS_BUILDIMAGES_SUFFIX: "" - TEST_INFRA_DEFINITIONS_BUILDIMAGES: c8eaaee94472 + TEST_INFRA_DEFINITIONS_BUILDIMAGES: 4650ea56cf36 DATADOG_AGENT_BUILDERS: v22276738-b36b132 DATADOG_AGENT_EMBEDDED_PATH: /opt/datadog-agent/embedded diff --git a/test/new-e2e/go.mod b/test/new-e2e/go.mod index 6412050056886..279bfa5d4a437 100644 --- a/test/new-e2e/go.mod +++ b/test/new-e2e/go.mod @@ -22,7 +22,7 @@ require ( // `TEST_INFRA_DEFINITIONS_BUILDIMAGES` matches the commit sha in the module version // Example: github.com/DataDog/test-infra-definitions v0.0.0-YYYYMMDDHHmmSS-0123456789AB // => TEST_INFRA_DEFINITIONS_BUILDIMAGES: 0123456789AB - github.com/DataDog/test-infra-definitions v0.0.0-20231123150722-c8eaaee94472 + github.com/DataDog/test-infra-definitions v0.0.0-20231127165329-4650ea56cf36 github.com/aws/aws-sdk-go-v2 v1.22.1 github.com/aws/aws-sdk-go-v2/config v1.18.40 github.com/aws/aws-sdk-go-v2/service/ec2 v1.130.0 @@ -165,7 +165,7 @@ require ( github.com/pulumi/pulumi-awsx/sdk v1.0.6 // indirect github.com/pulumi/pulumi-command/sdk v0.9.2 // indirect github.com/pulumi/pulumi-docker/sdk/v3 v3.6.1 // indirect - github.com/pulumi/pulumi-eks/sdk v1.0.2 // indirect + github.com/pulumi/pulumi-eks/sdk v1.0.4 // indirect github.com/pulumi/pulumi-kubernetes/sdk/v3 v3.30.2 // indirect github.com/pulumi/pulumi-libvirt/sdk v0.4.0 // indirect // pulumi-random v4.14.0 uses GO 1.21: @@ -222,3 +222,5 @@ require ( sigs.k8s.io/yaml v1.3.0 // indirect sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 // indirect ) + +require github.com/pulumi/pulumi-tls/sdk/v4 v4.10.0 // indirect diff --git a/test/new-e2e/go.sum b/test/new-e2e/go.sum index 3f08ae954c697..7023952f8819b 100644 --- a/test/new-e2e/go.sum +++ b/test/new-e2e/go.sum @@ -12,8 +12,8 @@ github.com/DataDog/datadog-api-client-go/v2 v2.15.0 h1:5UVON1xs6Lul4d6R5TmLDqqSJ github.com/DataDog/datadog-api-client-go/v2 v2.15.0/go.mod h1:ZG8wS+y2rUmkRDJZQq7Og7EAPFPage+7vXcmuah2I9o= github.com/DataDog/mmh3 v0.0.0-20200805151601-30884ca2197a h1:m9REhmyaWD5YJ0P53ygRHxKKo+KM+nw+zz0hEdKztMo= github.com/DataDog/mmh3 v0.0.0-20200805151601-30884ca2197a/go.mod h1:SvsjzyJlSg0rKsqYgdcFxeEVflx3ZNAyFfkUHP0TxXg= -github.com/DataDog/test-infra-definitions v0.0.0-20231123150722-c8eaaee94472 h1:Vl2osi/6xOlZGjtQ9tD1Jw6705vd3IDkehXr/EBmRPM= -github.com/DataDog/test-infra-definitions v0.0.0-20231123150722-c8eaaee94472/go.mod h1:PAsIkmkyjq2GD12gwu8gmjl6AdRH3IT/VMy3ycSOiG8= +github.com/DataDog/test-infra-definitions v0.0.0-20231127165329-4650ea56cf36 h1:eidj3lBZ7wvGdVCvr7MlZ9FcdTFpj8C4yBkh5qtI4gU= +github.com/DataDog/test-infra-definitions v0.0.0-20231127165329-4650ea56cf36/go.mod h1:P6FDz6Iyki8kX1WoDln5U+3kVLC+QzRi7FIzauuZ6jM= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/DataDog/zstd_0 v0.0.0-20210310093942-586c1286621f h1:5Vuo4niPKFkfwW55jV4vY0ih3VQ9RaQqeqY67fvRn8A= @@ -444,8 +444,8 @@ github.com/pulumi/pulumi-command/sdk v0.9.2 h1:2siCFR8pS2sSwXkeWiLrprGEtBL54FsHT github.com/pulumi/pulumi-command/sdk v0.9.2/go.mod h1:VeUXTI/iTgKVjRChRJbLRlBVGxAH+uymscfwzBC2VqY= github.com/pulumi/pulumi-docker/sdk/v3 v3.6.1 h1:plWLn9O6u80Vr37LoCsckyobBfcrdTU9cERor72QjqA= github.com/pulumi/pulumi-docker/sdk/v3 v3.6.1/go.mod h1:N4Yu4c49QErfucPt9Y/fGmpTryRqc0VfhyKHsGR9/g8= -github.com/pulumi/pulumi-eks/sdk v1.0.2 h1:Kg+AwcuME7adnFiIDZogdAf//vIGDmhGP6PHL7lddEc= -github.com/pulumi/pulumi-eks/sdk v1.0.2/go.mod h1:UwT4cRUztFr1//bOp0ACe3Cb3HOzRlX8sYz1psPWHHM= +github.com/pulumi/pulumi-eks/sdk v1.0.4 h1:j2tul6k0oZHDQwHU+75Jo8Qe4neYxv2hNpo5uanywrQ= +github.com/pulumi/pulumi-eks/sdk v1.0.4/go.mod h1:eSRoTIxvvu+uyc4tXo//TCsE9qD/DUx+OSLiyZvRB/A= github.com/pulumi/pulumi-kubernetes/sdk/v3 v3.17.0/go.mod h1:w+Y1d8uqc+gv7JYWLF4rfzvTsIIHR1SCL+GG6sX1xMM= github.com/pulumi/pulumi-kubernetes/sdk/v3 v3.30.2 h1:xJu48+RW+BHHnKtBni6Vj5vKqOEgCzdZAysGbh6tVM0= github.com/pulumi/pulumi-kubernetes/sdk/v3 v3.30.2/go.mod h1:7yCJFC/jnUwFs566f0FAY2iAzc4G1mQP8H6K+40FK4Y= @@ -453,6 +453,8 @@ github.com/pulumi/pulumi-libvirt/sdk v0.4.0 h1:wq1Ox8FRKQ1kc2DPq3m5DGQgZEhE7kp4m github.com/pulumi/pulumi-libvirt/sdk v0.4.0/go.mod h1:tjjyDajp6Pb1pRCdaIugknIfzxw3Prev3o/k2nade+I= github.com/pulumi/pulumi-random/sdk/v4 v4.13.4 h1:g3jdktE5L5IDrOw4OiB+yhgxSw0okRPJnyV6PlIzTEQ= github.com/pulumi/pulumi-random/sdk/v4 v4.13.4/go.mod h1:cFlJw0eQnqN+62QpITEF9M08gVyzNCeXrKRsuJptFak= +github.com/pulumi/pulumi-tls/sdk/v4 v4.10.0 h1:4MC0GyEomAjEZJPXEzBZpZ4+TOUg5WE77k38tMDIvS0= +github.com/pulumi/pulumi-tls/sdk/v4 v4.10.0/go.mod h1:tNXsM/+RsiVVmBdzJMOOp6gMoi3sPko5u0FKdiei+cE= github.com/pulumi/pulumi/sdk/v3 v3.16.0/go.mod h1:252ou/zAU1g6E8iTwe2Y9ht7pb5BDl2fJlOuAgZCHiA= github.com/pulumi/pulumi/sdk/v3 v3.50.1/go.mod h1:tqQ4z9ocyM/UI2VQ7ZReWR3w6dF5ffEozoHipOMcDh4= github.com/pulumi/pulumi/sdk/v3 v3.84.0 h1:/vCRj6ATGVZw4pFmG7pZgjlKUcnbbnb9vmlqd+OpdXo= From dd90301292f2cc2fec074bdebcc691bfbdd0ad31 Mon Sep 17 00:00:00 2001 From: Kevin Fairise <132568982+KevinFairise2@users.noreply.github.com> Date: Tue, 28 Nov 2023 00:18:51 +0100 Subject: [PATCH 38/87] Fix Heroku e2e tests (#21128) * Fix Heroku e2e tests * Do not run on security agent on CWS --- .../install-script/install_script_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/new-e2e/tests/agent-platform/install-script/install_script_test.go b/test/new-e2e/tests/agent-platform/install-script/install_script_test.go index 576eacd1f0f56..0349f689617d5 100644 --- a/test/new-e2e/tests/agent-platform/install-script/install_script_test.go +++ b/test/new-e2e/tests/agent-platform/install-script/install_script_test.go @@ -86,9 +86,9 @@ func TestInstallScript(t *testing.T) { func (is *installScriptSuite) TestInstallAgent() { switch *flavor { case "datadog-agent": - is.AgentTest() + is.AgentTest("datadog-agent") case "datadog-heroku-agent": - is.AgentTest() + is.AgentTest("datadog-heroku-agent") case "datadog-iot-agent": is.IotAgentTest() case "datadog-dogstatsd": @@ -96,7 +96,7 @@ func (is *installScriptSuite) TestInstallAgent() { } } -func (is *installScriptSuite) AgentTest() { +func (is *installScriptSuite) AgentTest(flavor string) { fileManager := filemanager.NewUnixFileManager(is.Env().VM) vm := is.Env().VM.(*client.PulumiStackVM) @@ -106,7 +106,7 @@ func (is *installScriptSuite) AgentTest() { unixHelper := helpers.NewUnixHelper() client := common.NewTestClient(is.Env().VM, agentClient, fileManager, unixHelper) - install.Unix(is.T(), client, installparams.WithArch(*architecture), installparams.WithFlavor(*flavor)) + install.Unix(is.T(), client, installparams.WithArch(*architecture), installparams.WithFlavor(flavor)) common.CheckInstallation(is.T(), client) common.CheckAgentBehaviour(is.T(), client) @@ -116,11 +116,11 @@ func (is *installScriptSuite) AgentTest() { common.CheckAgentPython(is.T(), client, "3") common.CheckApmEnabled(is.T(), client) common.CheckApmDisabled(is.T(), client) - if is.cwsSupported { + if flavor == "datadog-agent" && is.cwsSupported { common.CheckCWSBehaviour(is.T(), client) } common.CheckInstallationInstallScript(is.T(), client) - common.CheckUninstallation(is.T(), client, "datadog-agent") + common.CheckUninstallation(is.T(), client, flavor) } From 3316b1a8fce1e4600d3433644517a853a9722b58 Mon Sep 17 00:00:00 2001 From: Guy Arbitman Date: Tue, 28 Nov 2023 06:05:14 +0200 Subject: [PATCH 39/87] usm: tests: Fixed a flaky test (#21076) --- pkg/network/usm/monitor_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/network/usm/monitor_test.go b/pkg/network/usm/monitor_test.go index b53137822cf1f..32979f26b31fc 100644 --- a/pkg/network/usm/monitor_test.go +++ b/pkg/network/usm/monitor_test.go @@ -617,7 +617,8 @@ func (s *USMHTTP2Suite) TestHTTP2ManyDifferentPaths() { defer monitor.Stop() // Should be bigger than the length of the http2_dynamic_table which is 1024 - numberOfRequests := 1500 + const numberOfRequests = 1500 + const expectedNumberOfRequests = numberOfRequests * 2 clients := getClientsArray(t, 1) for i := 0; i < numberOfRequests; i++ { for j := 0; j < 2; j++ { @@ -647,7 +648,9 @@ func (s *USMHTTP2Suite) TestHTTP2ManyDifferentPaths() { } } - return matches.Load() == 2*numberOfRequests + // Due to a known issue in http2, we might consider an RST packet as a response to a request and therefore + // we might capture a request twice. This is why we are expecting to see 2*numberOfRequests instead of + return expectedNumberOfRequests <= matches.Load() && matches.Load() <= expectedNumberOfRequests+1 }, time.Second*10, time.Millisecond*100, "%v != %v", &matches, 2*numberOfRequests) for i := 0; i < numberOfRequests; i++ { From 59f519af2506eedc80f9d9e861d86aabb917f92f Mon Sep 17 00:00:00 2001 From: Guy Arbitman Date: Tue, 28 Nov 2023 06:05:44 +0200 Subject: [PATCH 40/87] usm: map cleaner: Use batch operations (#20907) * usm: map cleaner: Use batch operations Using batch operations allowing us to cut allocations, memory pressure and general runtime by 50%. * map cleaner: Shrink keysToDelete at the end of the operations * map cleaner: Remove useTimeStamps flag * map cleaner: Restore comment * map cleaner: Added documentation for the existence of 2 clean options * map cleaner: Improve shrinking * fixed CR * Remove redundant change * Addressed CR comment --- pkg/ebpf/map_cleaner.go | 172 +++++++++++------------- pkg/ebpf/map_cleaner_test.go | 111 ++++++++++++++- pkg/network/protocols/http/protocol.go | 15 +-- pkg/network/protocols/http2/protocol.go | 28 ++-- pkg/network/usm/ebpf_main.go | 26 ++-- 5 files changed, 218 insertions(+), 134 deletions(-) diff --git a/pkg/ebpf/map_cleaner.go b/pkg/ebpf/map_cleaner.go index 54e0c6a57bbb4..38e5e117c39c2 100644 --- a/pkg/ebpf/map_cleaner.go +++ b/pkg/ebpf/map_cleaner.go @@ -8,34 +8,24 @@ package ebpf import ( - "bytes" - "encoding" - "encoding/binary" - "errors" - "fmt" - "reflect" "sync" "time" "unsafe" cebpf "github.com/cilium/ebpf" + "github.com/DataDog/datadog-agent/pkg/util/kernel" "github.com/DataDog/datadog-agent/pkg/util/log" - "github.com/DataDog/datadog-agent/pkg/util/native" ) // MapCleaner is responsible for periodically sweeping an eBPF map // and deleting entries that satisfy a certain predicate function supplied by the user -type MapCleaner struct { - emap *cebpf.Map - key interface{} - val interface{} - once sync.Once +type MapCleaner[K any, V any] struct { + emap *cebpf.Map + keyBatch []K + valuesBatch []V - // we resort to unsafe.Pointers because by doing so the underlying eBPF - // library avoids marshaling the key/value variables while traversing the map - keyPtr unsafe.Pointer - valPtr unsafe.Pointer + once sync.Once // termination stopOnce sync.Once @@ -43,38 +33,44 @@ type MapCleaner struct { } // NewMapCleaner instantiates a new MapCleaner -func NewMapCleaner(emap *cebpf.Map, key, val interface{}) (*MapCleaner, error) { - // we force types to be of pointer kind because of the reasons mentioned above - if reflect.ValueOf(key).Kind() != reflect.Ptr { - return nil, fmt.Errorf("%T is not a pointer kind", key) +func NewMapCleaner[K any, V any](emap *cebpf.Map, defaultBatchSize uint32) (*MapCleaner[K, V], error) { + batchSize := defaultBatchSize + if defaultBatchSize > emap.MaxEntries() { + batchSize = emap.MaxEntries() } - if reflect.ValueOf(val).Kind() != reflect.Ptr { - return nil, fmt.Errorf("%T is not a pointer kind", val) + if batchSize == 0 { + batchSize = 1 } - return &MapCleaner{ - emap: emap, - key: key, - val: val, - keyPtr: unsafe.Pointer(reflect.ValueOf(key).Elem().Addr().Pointer()), - valPtr: unsafe.Pointer(reflect.ValueOf(val).Elem().Addr().Pointer()), - done: make(chan struct{}), + return &MapCleaner[K, V]{ + emap: emap, + keyBatch: make([]K, batchSize), + valuesBatch: make([]V, batchSize), + done: make(chan struct{}), }, nil } // Clean eBPF map // `interval` determines how often the eBPF map is scanned; -// `shouldClean` is a predicate method that determines whether or not a certain +// `shouldClean` is a predicate method that determines whether a certain // map entry should be deleted. the callback argument `nowTS` can be directly // compared to timestamps generated using the `bpf_ktime_get_ns()` helper; // `preClean` callback (optional, can pass nil) is invoked before the map is scanned; if it returns false, // the map is not scanned; this can be used to synchronize with other maps, or preform preliminary checks. // `postClean` callback (optional, can pass nil) is invoked after the map is scanned, to allow resource cleanup. -func (mc *MapCleaner) Clean(interval time.Duration, preClean func() bool, postClean func(), shouldClean func(nowTS int64, k, v interface{}) bool) { +func (mc *MapCleaner[K, V]) Clean(interval time.Duration, preClean func() bool, postClean func(), shouldClean func(nowTS int64, k K, v V) bool) { if mc == nil { return } + // Since kernel 5.6, the eBPF library supports batch operations on maps, which reduces the number of syscalls + // required to clean the map. We use the new batch operations if the kernel version is >= 5.6, and fallback to + // the old method otherwise. The new API is also more efficient because it minimizes the number of allocations. + cleaner := mc.cleanWithoutBatches + if version, err := kernel.HostVersion(); err == nil && version >= kernel.VersionCode(5, 6, 0) { + cleaner = mc.cleanWithBatches + } + mc.once.Do(func() { ticker := time.NewTicker(interval) go func() { @@ -91,7 +87,7 @@ func (mc *MapCleaner) Clean(interval time.Duration, preClean func() bool, postCl if preClean != nil && !preClean() { continue } - mc.clean(now, shouldClean) + cleaner(now, shouldClean) // Allowing cleanup after the cleanup. if postClean != nil { postClean() @@ -105,7 +101,7 @@ func (mc *MapCleaner) Clean(interval time.Duration, preClean func() bool, postCl } // Stop stops the map cleaner -func (mc *MapCleaner) Stop() { +func (mc *MapCleaner[K, V]) Stop() { if mc == nil { return } @@ -117,85 +113,81 @@ func (mc *MapCleaner) Stop() { }) } -func (mc *MapCleaner) clean(nowTS int64, shouldClean func(nowTS int64, k, v interface{}) bool) { - keySize := int(mc.emap.KeySize()) - keysToDelete := make([][]byte, 0, 128) - totalCount, deletedCount := 0, 0 +func (mc *MapCleaner[K, V]) cleanWithBatches(nowTS int64, shouldClean func(nowTS int64, k K, v V) bool) { now := time.Now() - entries := mc.emap.Iterate() - for entries.Next(mc.keyPtr, mc.valPtr) { - totalCount++ - - if !shouldClean(nowTS, mc.key, mc.val) { - continue + var keysToDelete []K + totalCount, deletedCount := 0, 0 + var next K + var n int + for { + n, _ = mc.emap.BatchLookup(next, &next, mc.keyBatch, mc.valuesBatch, nil) + if n == 0 { + break } - marshalledKey, err := marshalBytes(mc.key, keySize) - if err != nil { - continue + totalCount += n + for i := 0; i < n; i++ { + if !shouldClean(nowTS, mc.keyBatch[i], mc.valuesBatch[i]) { + continue + } + keysToDelete = append(keysToDelete, mc.keyBatch[i]) } - // we accumulate alll keys to delete because it isn't safe to delete map - // entries during the traversal. the main downside of doing so is that all - // fields from the key type must be exported in order to be marshaled (unless - // the key type implements the `encoding.BinaryMarshaler` interface) - keysToDelete = append(keysToDelete, marshalledKey) + // Just a safety check to avoid an infinite loop. + if totalCount >= int(mc.emap.MaxEntries()) { + break + } } - - for _, key := range keysToDelete { - err := mc.emap.Delete(key) - if err == nil { - deletedCount++ + if len(keysToDelete) > 0 { + count, err := mc.emap.BatchDelete(keysToDelete, nil) + if err != nil { + log.Debugf("failed to delete map entries: %v", err) + return } + deletedCount += count } - iterationErr := entries.Err() elapsed := time.Since(now) log.Debugf( - "finished cleaning map=%s entries_checked=%d entries_deleted=%d iteration_error=%v elapsed=%s", + "finished cleaning map=%s entries_checked=%d entries_deleted=%d elapsed=%s", mc.emap, totalCount, deletedCount, - iterationErr, elapsed, ) } -// marshalBytes converts an arbitrary value into a byte buffer. -// -// Returns an error if the given value isn't representable in exactly -// length bytes. -// -// copied from: https://github.com/cilium/ebpf/blob/master/marshalers.go -func marshalBytes(data interface{}, length int) (buf []byte, err error) { - if data == nil { - return nil, errors.New("can't marshal a nil value") - } +func (mc *MapCleaner[K, V]) cleanWithoutBatches(nowTS int64, shouldClean func(nowTS int64, k K, v V) bool) { + now := time.Now() - switch value := data.(type) { - case encoding.BinaryMarshaler: - buf, err = value.MarshalBinary() - case string: - buf = []byte(value) - case []byte: - buf = value - case unsafe.Pointer: - err = errors.New("can't marshal from unsafe.Pointer") - default: - var wr bytes.Buffer - err = binary.Write(&wr, native.Endian, value) - if err != nil { - err = fmt.Errorf("encoding %T: %v", value, err) + var keysToDelete []K + totalCount, deletedCount := 0, 0 + + entries := mc.emap.Iterate() + // we resort to unsafe.Pointers because by doing so the underlying eBPF + // library avoids marshaling the key/value variables while traversing the map + for entries.Next(unsafe.Pointer(&mc.keyBatch[0]), unsafe.Pointer(&mc.valuesBatch[0])) { + totalCount++ + if !shouldClean(nowTS, mc.keyBatch[0], mc.valuesBatch[0]) { + continue } - buf = wr.Bytes() - } - if err != nil { - return nil, err + keysToDelete = append(keysToDelete, mc.keyBatch[0]) } - if len(buf) != length { - return nil, fmt.Errorf("%T doesn't marshal to %d bytes", data, length) + for _, key := range keysToDelete { + err := mc.emap.Delete(unsafe.Pointer(&key)) + if err == nil { + deletedCount++ + } } - return buf, nil + + elapsed := time.Since(now) + log.Debugf( + "finished cleaning map=%s entries_checked=%d entries_deleted=%d elapsed=%s", + mc.emap, + totalCount, + deletedCount, + elapsed, + ) } diff --git a/pkg/ebpf/map_cleaner_test.go b/pkg/ebpf/map_cleaner_test.go index b123789f042da..45fc6be3eb83d 100644 --- a/pkg/ebpf/map_cleaner_test.go +++ b/pkg/ebpf/map_cleaner_test.go @@ -8,15 +8,28 @@ package ebpf import ( + "os" "testing" "time" + "github.com/cihub/seelog" cebpf "github.com/cilium/ebpf" "github.com/cilium/ebpf/rlimit" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/DataDog/datadog-agent/pkg/util/log" ) +func TestMain(m *testing.M) { + logLevel := os.Getenv("DD_LOG_LEVEL") + if logLevel == "" { + logLevel = "warn" + } + log.SetupLogger(seelog.Default, logLevel) + os.Exit(m.Run()) +} + func TestMapCleaner(t *testing.T) { const numMapEntries = 100 @@ -36,7 +49,7 @@ func TestMapCleaner(t *testing.T) { }) require.NoError(t, err) - cleaner, err := NewMapCleaner(m, key, val) + cleaner, err := NewMapCleaner[int64, int64](m, 10) require.NoError(t, err) for i := 0; i < numMapEntries; i++ { *key = int64(i) @@ -45,9 +58,8 @@ func TestMapCleaner(t *testing.T) { } // Clean all the even entries - cleaner.Clean(100*time.Millisecond, nil, nil, func(now int64, k, v interface{}) bool { - key := k.(*int64) - return *key%2 == 0 + cleaner.Clean(100*time.Millisecond, nil, nil, func(now int64, k int64, v int64) bool { + return k%2 == 0 }) time.Sleep(1 * time.Second) @@ -66,3 +78,94 @@ func TestMapCleaner(t *testing.T) { } } } + +func benchmarkBatchCleaner(b *testing.B, numMapEntries, batchSize uint32) { + var ( + key = new(int64) + val = new(int64) + ) + + err := rlimit.RemoveMemlock() + require.NoError(b, err) + + m, err := cebpf.NewMap(&cebpf.MapSpec{ + Type: cebpf.Hash, + KeySize: 8, + ValueSize: 8, + MaxEntries: numMapEntries, + }) + require.NoError(b, err) + + cleaner, err := NewMapCleaner[int64, int64](m, batchSize) + require.NoError(b, err) + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + for i := uint32(0); i < numMapEntries; i++ { + *key = int64(i) + err := m.Put(key, val) + assert.Nilf(b, err, "can't put key=%d: %s", i, err) + } + + // Clean all the even entries + if batchSize == 0 { + cleaner.cleanWithoutBatches(0, func(now int64, k int64, v int64) bool { + return k%2 == 0 + }) + } else { + cleaner.cleanWithBatches(0, func(now int64, k int64, v int64) bool { + return k%2 == 0 + }) + } + for i := uint32(0); i < numMapEntries; i++ { + *key = int64(i) + err := m.Lookup(key, val) + + // If the entry is even, it should have been deleted + // otherwise it should be present + if i%2 == 0 { + assert.NotNilf(b, err, "entry key=%d should not be present", i) + } else { + assert.Nil(b, err) + } + } + } +} + +func BenchmarkBatchCleaner1000Entries10PerBatch(b *testing.B) { + benchmarkBatchCleaner(b, 1000, 10) +} + +func BenchmarkBatchCleaner1000Entries100PerBatch(b *testing.B) { + benchmarkBatchCleaner(b, 1000, 100) +} + +func BenchmarkBatchCleaner10000Entries100PerBatch(b *testing.B) { + benchmarkBatchCleaner(b, 10000, 100) +} + +func BenchmarkBatchCleaner10000Entries1000PerBatch(b *testing.B) { + benchmarkBatchCleaner(b, 10000, 1000) +} + +func BenchmarkBatchCleaner100000Entries100PerBatch(b *testing.B) { + benchmarkBatchCleaner(b, 100000, 100) +} + +func BenchmarkBatchCleaner100000Entries1000PerBatch(b *testing.B) { + benchmarkBatchCleaner(b, 100000, 1000) +} + +func BenchmarkCleaner1000Entries(b *testing.B) { + benchmarkBatchCleaner(b, 1000, 0) +} + +func BenchmarkCleaner10000Entries(b *testing.B) { + benchmarkBatchCleaner(b, 10000, 0) +} + +func BenchmarkCleaner100000Entries(b *testing.B) { + benchmarkBatchCleaner(b, 100000, 0) +} diff --git a/pkg/network/protocols/http/protocol.go b/pkg/network/protocols/http/protocol.go index 2a058b0ed8ccf..c37e1e24d4a83 100644 --- a/pkg/network/protocols/http/protocol.go +++ b/pkg/network/protocols/http/protocol.go @@ -32,7 +32,7 @@ type protocol struct { cfg *config.Config telemetry *Telemetry statkeeper *StatKeeper - mapCleaner *ddebpf.MapCleaner + mapCleaner *ddebpf.MapCleaner[netebpf.ConnTuple, EbpfTx] eventsConsumer *events.Consumer } @@ -175,24 +175,19 @@ func (p *protocol) setupMapCleaner(mgr *manager.Manager) { log.Errorf("error getting http_in_flight map: %s", err) return } - mapCleaner, err := ddebpf.NewMapCleaner(httpMap, new(netebpf.ConnTuple), new(EbpfTx)) + mapCleaner, err := ddebpf.NewMapCleaner[netebpf.ConnTuple, EbpfTx](httpMap, 1024) if err != nil { log.Errorf("error creating map cleaner: %s", err) return } ttl := p.cfg.HTTPIdleConnectionTTL.Nanoseconds() - mapCleaner.Clean(p.cfg.HTTPMapCleanerInterval, nil, nil, func(now int64, key, val interface{}) bool { - httpTxn, ok := val.(*EbpfTx) - if !ok { - return false - } - - if updated := int64(httpTxn.Response_last_seen); updated > 0 { + mapCleaner.Clean(p.cfg.HTTPMapCleanerInterval, nil, nil, func(now int64, key netebpf.ConnTuple, val EbpfTx) bool { + if updated := int64(val.Response_last_seen); updated > 0 { return (now - updated) > ttl } - started := int64(httpTxn.Request_started) + started := int64(val.Request_started) return started > 0 && (now-started) > ttl }) diff --git a/pkg/network/protocols/http2/protocol.go b/pkg/network/protocols/http2/protocol.go index 0bdc6c4369129..d39baf549f870 100644 --- a/pkg/network/protocols/http2/protocol.go +++ b/pkg/network/protocols/http2/protocol.go @@ -37,8 +37,8 @@ type protocol struct { telemetry *http.Telemetry // TODO: Do we need to duplicate? statkeeper *http.StatKeeper - http2InFlightMapCleaner *ddebpf.MapCleaner - dynamicTableMapCleaner *ddebpf.MapCleaner + http2InFlightMapCleaner *ddebpf.MapCleaner[http2StreamKey, EbpfTx] + dynamicTableMapCleaner *ddebpf.MapCleaner[http2DynamicTableIndex, http2DynamicTableEntry] eventsConsumer *events.Consumer terminatedConnectionsEventsConsumer *events.Consumer @@ -267,24 +267,19 @@ func (p *protocol) setupHTTP2InFlightMapCleaner(mgr *manager.Manager) { log.Errorf("error getting %q map: %s", inFlightMap, err) return } - mapCleaner, err := ddebpf.NewMapCleaner(http2Map, new(http2StreamKey), new(EbpfTx)) + mapCleaner, err := ddebpf.NewMapCleaner[http2StreamKey, EbpfTx](http2Map, 1024) if err != nil { log.Errorf("error creating map cleaner: %s", err) return } ttl := p.cfg.HTTPIdleConnectionTTL.Nanoseconds() - mapCleaner.Clean(p.cfg.HTTPMapCleanerInterval, nil, nil, func(now int64, key, val interface{}) bool { - http2Txn, ok := val.(*EbpfTx) - if !ok { - return false - } - - if updated := int64(http2Txn.Stream.Response_last_seen); updated > 0 { + mapCleaner.Clean(p.cfg.HTTPMapCleanerInterval, nil, nil, func(now int64, key http2StreamKey, val EbpfTx) bool { + if updated := int64(val.Stream.Response_last_seen); updated > 0 { return (now - updated) > ttl } - started := int64(http2Txn.Stream.Request_started) + started := int64(val.Stream.Request_started) return started > 0 && (now-started) > ttl }) @@ -298,7 +293,7 @@ func (p *protocol) setupDynamicTableMapCleaner(mgr *manager.Manager) { return } - mapCleaner, err := ddebpf.NewMapCleaner(dynamicTableMap, new(http2DynamicTableIndex), new(http2DynamicTableEntry)) + mapCleaner, err := ddebpf.NewMapCleaner[http2DynamicTableIndex, http2DynamicTableEntry](dynamicTableMap, 1024) if err != nil { log.Errorf("error creating map cleaner: %s", err) return @@ -320,13 +315,8 @@ func (p *protocol) setupDynamicTableMapCleaner(mgr *manager.Manager) { func() { terminatedConnectionsMap = make(map[netebpf.ConnTuple]struct{}) }, - func(now int64, key, val interface{}) bool { - keyIndex, ok := key.(*http2DynamicTableIndex) - if !ok { - return false - } - - _, ok = terminatedConnectionsMap[keyIndex.Tup] + func(_ int64, key http2DynamicTableIndex, _ http2DynamicTableEntry) bool { + _, ok := terminatedConnectionsMap[key.Tup] return ok }) p.dynamicTableMapCleaner = mapCleaner diff --git a/pkg/network/usm/ebpf_main.go b/pkg/network/usm/ebpf_main.go index 795fdd677c431..a3da4d7a3b1a5 100644 --- a/pkg/network/usm/ebpf_main.go +++ b/pkg/network/usm/ebpf_main.go @@ -78,7 +78,7 @@ type ebpfProgram struct { disabledProtocols []*protocols.ProtocolSpec // Used for connection_protocol data expiration - mapCleaner *ddebpf.MapCleaner + mapCleaner *ddebpf.MapCleaner[netebpf.ConnTuple, netebpf.ProtocolStackWrapper] buildMode buildmode.Type } @@ -170,6 +170,16 @@ func (e *ebpfProgram) Init() error { } func (e *ebpfProgram) Start() error { + // Mainly for tests, but possible for other cases as well, we might have a nil (not shared) connection protocol map + // between NPM and USM. In such a case we just create our own instance, but we don't modify the + // `e.connectionProtocolMap` field. + if e.connectionProtocolMap == nil { + m, _, err := e.GetMap(probes.ConnectionProtocolMap) + if err != nil { + return err + } + e.connectionProtocolMap = m + } mapCleaner, err := e.setupMapCleaner() if err != nil { log.Errorf("error creating map cleaner: %s", err) @@ -403,21 +413,15 @@ func (e *ebpfProgram) init(buf bytecode.AssetReader, options manager.Options) er const connProtoTTL = 3 * time.Minute const connProtoCleaningInterval = 5 * time.Minute -func (e *ebpfProgram) setupMapCleaner() (*ddebpf.MapCleaner, error) { - mapCleaner, err := ddebpf.NewMapCleaner(e.connectionProtocolMap, new(netebpf.ConnTuple), new(netebpf.ProtocolStackWrapper)) +func (e *ebpfProgram) setupMapCleaner() (*ddebpf.MapCleaner[netebpf.ConnTuple, netebpf.ProtocolStackWrapper], error) { + mapCleaner, err := ddebpf.NewMapCleaner[netebpf.ConnTuple, netebpf.ProtocolStackWrapper](e.connectionProtocolMap, 1024) if err != nil { return nil, err } ttl := connProtoTTL.Nanoseconds() - mapCleaner.Clean(connProtoCleaningInterval, nil, nil, func(now int64, key, val interface{}) bool { - protoStack, ok := val.(*netebpf.ProtocolStackWrapper) - if !ok { - return false - } - - updated := int64(protoStack.Updated) - return (now - updated) > ttl + mapCleaner.Clean(connProtoCleaningInterval, nil, nil, func(now int64, key netebpf.ConnTuple, val netebpf.ProtocolStackWrapper) bool { + return (now - int64(val.Updated)) > ttl }) return mapCleaner, nil From 2abd92219c5ee538a5d34ba9d763f316a9cbc2ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 08:49:16 +0100 Subject: [PATCH 41/87] Bump golang.org/x/sys from 0.14.0 to 0.15.0 in /pkg/security/secl (#21134) * Bump golang.org/x/sys from 0.14.0 to 0.15.0 in /pkg/security/secl Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.14.0 to 0.15.0. - [Commits](https://github.com/golang/sys/compare/v0.14.0...v0.15.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Auto-generate go.sum and LICENSE-3rdparty.csv changes --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- pkg/security/secl/go.mod | 2 +- pkg/security/secl/go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index b0b1d6609cfea..85501d69faa77 100644 --- a/go.mod +++ b/go.mod @@ -240,7 +240,7 @@ require ( golang.org/x/exp v0.0.0-20231006140011-7918f672742d golang.org/x/net v0.18.0 golang.org/x/sync v0.5.0 - golang.org/x/sys v0.14.1-0.20231108175955-e4099bfacb8c + golang.org/x/sys v0.15.0 golang.org/x/text v0.14.0 golang.org/x/time v0.3.0 golang.org/x/tools v0.15.0 diff --git a/go.sum b/go.sum index 0b72c63002e69..ba13f8e76d203 100644 --- a/go.sum +++ b/go.sum @@ -2102,8 +2102,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.1-0.20231108175955-e4099bfacb8c h1:3kC/TjQ+xzIblQv39bCOyRk8fbEeJcDHwbyxPUU2BpA= -golang.org/x/sys v0.14.1-0.20231108175955-e4099bfacb8c/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/pkg/security/secl/go.mod b/pkg/security/secl/go.mod index 48111ba9c8aea..f621eb9bc0287 100644 --- a/pkg/security/secl/go.mod +++ b/pkg/security/secl/go.mod @@ -16,7 +16,7 @@ require ( github.com/spf13/cast v1.5.1 github.com/stretchr/testify v1.8.4 golang.org/x/exp v0.0.0-20221114191408-850992195362 - golang.org/x/sys v0.14.0 + golang.org/x/sys v0.15.0 golang.org/x/text v0.14.0 golang.org/x/tools v0.15.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/pkg/security/secl/go.sum b/pkg/security/secl/go.sum index 11c886023fd75..9332b6d422e83 100644 --- a/pkg/security/secl/go.sum +++ b/pkg/security/secl/go.sum @@ -82,8 +82,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= From f19b09485a8bbbef803190bab6c34ad60d0d94e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 08:49:48 +0100 Subject: [PATCH 42/87] Bump golang.org/x/tools from 0.15.0 to 0.16.0 in /pkg/security/secl (#21133) * Bump golang.org/x/tools from 0.15.0 to 0.16.0 in /pkg/security/secl Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.15.0 to 0.16.0. - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.15.0...v0.16.0) --- updated-dependencies: - dependency-name: golang.org/x/tools dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Auto-generate go.sum and LICENSE-3rdparty.csv changes --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- pkg/security/secl/go.mod | 2 +- pkg/security/secl/go.sum | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 85501d69faa77..640848fec748d 100644 --- a/go.mod +++ b/go.mod @@ -238,12 +238,12 @@ require ( go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d golang.org/x/arch v0.6.0 golang.org/x/exp v0.0.0-20231006140011-7918f672742d - golang.org/x/net v0.18.0 + golang.org/x/net v0.19.0 golang.org/x/sync v0.5.0 golang.org/x/sys v0.15.0 golang.org/x/text v0.14.0 golang.org/x/time v0.3.0 - golang.org/x/tools v0.15.0 + golang.org/x/tools v0.16.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/grpc v1.59.0 @@ -546,10 +546,10 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.20.0 // indirect go.opentelemetry.io/otel/trace v1.20.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect - golang.org/x/crypto v0.15.0 // indirect + golang.org/x/crypto v0.16.0 // indirect golang.org/x/mod v0.14.0 golang.org/x/oauth2 v0.11.0 // indirect - golang.org/x/term v0.14.0 // indirect + golang.org/x/term v0.15.0 // indirect gonum.org/v1/gonum v0.14.0 // indirect google.golang.org/api v0.134.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index ba13f8e76d203..3b350a26177d4 100644 --- a/go.sum +++ b/go.sum @@ -1847,8 +1847,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1959,8 +1959,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2112,8 +2112,8 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= -golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2206,8 +2206,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= -golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/security/secl/go.mod b/pkg/security/secl/go.mod index f621eb9bc0287..4c13c1860617d 100644 --- a/pkg/security/secl/go.mod +++ b/pkg/security/secl/go.mod @@ -18,7 +18,7 @@ require ( golang.org/x/exp v0.0.0-20221114191408-850992195362 golang.org/x/sys v0.15.0 golang.org/x/text v0.14.0 - golang.org/x/tools v0.15.0 + golang.org/x/tools v0.16.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/pkg/security/secl/go.sum b/pkg/security/secl/go.sum index 9332b6d422e83..aa78e93db1957 100644 --- a/pkg/security/secl/go.sum +++ b/pkg/security/secl/go.sum @@ -96,8 +96,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= -golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From c03a6f7f88758b255e072cb8818b0be3d2e32ea0 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Tue, 28 Nov 2023 09:07:41 +0100 Subject: [PATCH 43/87] statsd: improve listener_id (#21110) * statsd: improve listener_id Always set to uds- * -> telemetryWithFullListenerID --- comp/dogstatsd/listeners/uds_common.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/comp/dogstatsd/listeners/uds_common.go b/comp/dogstatsd/listeners/uds_common.go index 5048dbeebf22e..53d5bfa3d1648 100644 --- a/comp/dogstatsd/listeners/uds_common.go +++ b/comp/dogstatsd/listeners/uds_common.go @@ -149,8 +149,10 @@ func NewUDSListener(packetOut chan packets.Packets, sharedPacketPoolManager *pac func (l *UDSListener) handleConnection(conn *net.UnixConn, closeFunc CloseFunction) error { listenerID := l.getListenerID(conn) tlmListenerID := listenerID - if !l.config.GetBool("dogstatsd_telemetry_enabled_listener_id") { - tlmListenerID = "" + telemetryWithFullListenerID := l.config.GetBool("dogstatsd_telemetry_enabled_listener_id") + if !telemetryWithFullListenerID { + // In case we don't want the full listener id, we only keep the transport. + tlmListenerID = "uds-" + conn.LocalAddr().Network() } packetsBuffer := packets.NewBuffer( @@ -163,7 +165,9 @@ func (l *UDSListener) handleConnection(conn *net.UnixConn, closeFunc CloseFuncti defer func() { _ = closeFunc(conn) packetsBuffer.Close() - l.clearTelemetry(tlmListenerID) + if telemetryWithFullListenerID { + l.clearTelemetry(tlmListenerID) + } tlmUDSConnections.Dec(tlmListenerID, l.transport) }() From 533daf7d073882e074308976a3ff29d434fe0558 Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Tue, 28 Nov 2023 10:08:29 +0100 Subject: [PATCH 44/87] [CWS] mount resolver cleanup and improvements (#21030) * switch to finalize instead of repeating delete * remove `pidToMounts` from mount resolver * allow device fallback only after procfs resync * fix tests * move redemption into fallbacks --- pkg/security/probe/probe_ebpf.go | 5 +- pkg/security/resolvers/mount/resolver.go | 104 +++++++----------- pkg/security/resolvers/mount/resolver_test.go | 8 +- 3 files changed, 45 insertions(+), 72 deletions(-) diff --git a/pkg/security/probe/probe_ebpf.go b/pkg/security/probe/probe_ebpf.go index 5dace2eff2f81..80b074272f105 100644 --- a/pkg/security/probe/probe_ebpf.go +++ b/pkg/security/probe/probe_ebpf.go @@ -760,9 +760,6 @@ func (p *EBPFProbe) handleEvent(CPU int, data []byte) { // The pid_cache kernel map has the exit_time but it's only accessed if there's a local miss event.ProcessCacheEntry.Process.ExitTime = p.fieldHandlers.ResolveEventTime(event, &event.BaseEvent) event.Exit.Process = &event.ProcessCacheEntry.Process - - // update mount pid mapping - p.Resolvers.MountResolver.DelPid(event.Exit.Pid) case model.SetuidEventType: // the process context may be incorrect, do not modify it if event.Error != nil { @@ -1336,7 +1333,7 @@ func (p *EBPFProbe) handleNewMount(ev *model.Event, m *model.Mount) error { } // Insert new mount point in cache, passing it a copy of the mount that we got from the event - if err := p.Resolvers.MountResolver.Insert(*m, 0); err != nil { + if err := p.Resolvers.MountResolver.Insert(*m); err != nil { seclog.Errorf("failed to insert mount event: %v", err) return err } diff --git a/pkg/security/resolvers/mount/resolver.go b/pkg/security/resolvers/mount/resolver.go index aef4685fc374a..d1edb070630ce 100644 --- a/pkg/security/resolvers/mount/resolver.go +++ b/pkg/security/resolvers/mount/resolver.go @@ -69,7 +69,6 @@ type Resolver struct { statsdClient statsd.ClientInterface lock sync.RWMutex mounts map[uint32]*model.Mount - pidToMounts map[uint32]map[uint32]*model.Mount minMountID uint32 // used to find the first userspace visible mount ID redemption *simplelru.LRU[uint32, *redemptionEntry] fallbackLimiter *utils.Limiter[uint64] @@ -120,13 +119,12 @@ func (mr *Resolver) syncPid(pid uint32) error { } for _, mnt := range mnts { - if m, exists := mr.mounts[uint32(mnt.ID)]; exists { - mr.updatePidMapping(m, pid) + if _, exists := mr.mounts[uint32(mnt.ID)]; exists { continue } m := newMountFromMountInfo(mnt) - mr.insert(m, pid) + mr.insert(m) } return nil @@ -160,7 +158,7 @@ func (mr *Resolver) delete(mount *model.Mount) { curr, rest := openQueue[len(openQueue)-1], openQueue[:len(openQueue)-1] openQueue = rest - delete(mr.mounts, curr.MountID) + mr.finalize(curr) entry := redemptionEntry{ mount: curr, @@ -173,10 +171,6 @@ func (mr *Resolver) delete(mount *model.Mount) { openQueue = append(openQueue, child) } } - - for _, mounts := range mr.pidToMounts { - delete(mounts, mount.MountID) - } } } @@ -212,7 +206,7 @@ func (mr *Resolver) ResolveFilesystem(mountID uint32, device uint32, pid uint32, } // Insert a new mount point in the cache -func (mr *Resolver) Insert(m model.Mount, pid uint32) error { +func (mr *Resolver) Insert(m model.Mount) error { if m.MountID == 0 { return ErrMountUndefined } @@ -220,37 +214,12 @@ func (mr *Resolver) Insert(m model.Mount, pid uint32) error { mr.lock.Lock() defer mr.lock.Unlock() - mr.insert(&m, pid) + mr.insert(&m) return nil } -func (mr *Resolver) updatePidMapping(m *model.Mount, pid uint32) { - if pid == 0 { - return - } - - mounts := mr.pidToMounts[pid] - if mounts == nil { - mounts = make(map[uint32]*model.Mount) - mr.pidToMounts[pid] = mounts - } - mounts[m.MountID] = m -} - -// DelPid removes the pid form the pid mapping -func (mr *Resolver) DelPid(pid uint32) { - if pid == 0 { - return - } - - mr.lock.Lock() - defer mr.lock.Unlock() - - delete(mr.pidToMounts, pid) -} - -func (mr *Resolver) insert(m *model.Mount, pid uint32) { +func (mr *Resolver) insert(m *model.Mount) { // umount the previous one if exists if prev, ok := mr.mounts[m.MountID]; ok { // put the prev entry and the all the children in the redemption list @@ -273,8 +242,6 @@ func (mr *Resolver) insert(m *model.Mount, pid uint32) { } mr.mounts[m.MountID] = m - - mr.updatePidMapping(m, pid) } func (mr *Resolver) getFromRedemption(mountID uint32) *model.Mount { @@ -286,20 +253,13 @@ func (mr *Resolver) getFromRedemption(mountID uint32) *model.Mount { } func (mr *Resolver) lookupByMountID(mountID uint32) *model.Mount { - mount := mr.mounts[mountID] - if mount != nil { - return mount - } - - return mr.getFromRedemption(mountID) + return mr.mounts[mountID] } -func (mr *Resolver) lookupByDevice(device uint32, pid uint32) *model.Mount { +func (mr *Resolver) lookupByDevice(device uint32) *model.Mount { var result *model.Mount - mounts := mr.pidToMounts[pid] - - for _, mount := range mounts { + for _, mount := range mr.mounts { if mount.Device == device { // should be consistent across all the mounts if result != nil && result.MountPointStr != mount.MountPointStr { @@ -312,21 +272,30 @@ func (mr *Resolver) lookupByDevice(device uint32, pid uint32) *model.Mount { return result } -func (mr *Resolver) lookupMount(mountID uint32, device uint32, pid uint32) *model.Mount { - mount := mr.lookupByMountID(mountID) - if mount != nil { - return mount +func (mr *Resolver) lookupMount(mountID uint32, device uint32, allowFallbacks bool) *model.Mount { + if m := mr.lookupByMountID(mountID); m != nil { + return m + } + + if allowFallbacks { + if m := mr.lookupByDevice(device); m != nil { + return m + } + + if m := mr.getFromRedemption(mountID); m != nil { + return m + } } - return mr.lookupByDevice(device, pid) + return nil } -func (mr *Resolver) _getMountPath(mountID uint32, device uint32, pid uint32, cache map[uint32]bool) (string, error) { +func (mr *Resolver) _getMountPath(mountID uint32, device uint32, cache map[uint32]bool, allowFallbacks bool) (string, error) { if _, err := mr.IsMountIDValid(mountID); err != nil { return "", err } - mount := mr.lookupMount(mountID, device, pid) + mount := mr.lookupMount(mountID, device, allowFallbacks) if mount == nil { return "", &ErrMountNotFound{MountID: mountID} } @@ -350,7 +319,7 @@ func (mr *Resolver) _getMountPath(mountID uint32, device uint32, pid uint32, cac return "", ErrMountUndefined } - parentMountPath, err := mr._getMountPath(mount.ParentPathKey.MountID, mount.Device, pid, cache) + parentMountPath, err := mr._getMountPath(mount.ParentPathKey.MountID, mount.Device, cache, allowFallbacks) if err != nil { return "", err } @@ -365,8 +334,8 @@ func (mr *Resolver) _getMountPath(mountID uint32, device uint32, pid uint32, cac return mountPointStr, nil } -func (mr *Resolver) getMountPath(mountID uint32, device uint32, pid uint32) (string, error) { - return mr._getMountPath(mountID, device, pid, map[uint32]bool{}) +func (mr *Resolver) getMountPath(mountID uint32, device uint32, allowDeviceFallback bool) (string, error) { + return mr._getMountPath(mountID, device, map[uint32]bool{}, allowDeviceFallback) } // ResolveMountRoot returns the root of a mount identified by its mount ID. @@ -420,7 +389,11 @@ func (mr *Resolver) resolveMountPath(mountID uint32, device uint32, pid uint32, // force a resolution here to make sure the LRU keeps doing its job and doesn't evict important entries workload, _ := mr.cgroupsResolver.GetWorkload(containerID) - path, err := mr.getMountPath(mountID, device, pid) + // if UseProcFS is disabled, we can directly allow the device fallback and redemption since getMountPath will be called + // only once + allowFallbacks := !mr.opts.UseProcFS + + path, err := mr.getMountPath(mountID, device, allowFallbacks) if err == nil { mr.cacheHitsStats.Inc() return path, nil @@ -435,7 +408,7 @@ func (mr *Resolver) resolveMountPath(mountID uint32, device uint32, pid uint32, return "", err } - path, err = mr.getMountPath(mountID, device, pid) + path, err = mr.getMountPath(mountID, device, true) if err == nil { mr.procHitsStats.Inc() return path, nil @@ -461,7 +434,11 @@ func (mr *Resolver) resolveMount(mountID uint32, device uint32, pid uint32, cont // force a resolution here to make sure the LRU keeps doing its job and doesn't evict important entries workload, _ := mr.cgroupsResolver.GetWorkload(containerID) - mount := mr.lookupMount(mountID, device, pid) + // if UseProcFS is disabled, we can directly allow the device fallback and redemption since getMountPath will be called + // only once + allowFallbacks := !mr.opts.UseProcFS + + mount := mr.lookupMount(mountID, device, allowFallbacks) if mount != nil { mr.cacheHitsStats.Inc() return mount, nil @@ -476,7 +453,7 @@ func (mr *Resolver) resolveMount(mountID uint32, device uint32, pid uint32, cont return nil, err } - mount = mr.mounts[mountID] + mount = mr.lookupMount(mountID, device, true) if mount != nil { mr.procHitsStats.Inc() return mount, nil @@ -598,7 +575,6 @@ func NewResolver(statsdClient statsd.ClientInterface, cgroupsResolver *cgroup.Re cgroupsResolver: cgroupsResolver, lock: sync.RWMutex{}, mounts: make(map[uint32]*model.Mount), - pidToMounts: make(map[uint32]map[uint32]*model.Mount), cacheHitsStats: atomic.NewInt64(0), procHitsStats: atomic.NewInt64(0), cacheMissStats: atomic.NewInt64(0), diff --git a/pkg/security/resolvers/mount/resolver_test.go b/pkg/security/resolvers/mount/resolver_test.go index 517872059e945..2ff231ed3b613 100644 --- a/pkg/security/resolvers/mount/resolver_test.go +++ b/pkg/security/resolvers/mount/resolver_test.go @@ -444,7 +444,7 @@ func TestMountResolver(t *testing.T) { t.Run(tt.name, func(t *testing.T) { for _, evt := range tt.args.events { if evt.mount != nil { - mr.insert(&evt.mount.Mount, pid) + mr.insert(&evt.mount.Mount) } if evt.umount != nil { mount, err := mr.ResolveMount(evt.umount.MountID, 0, pid, "") @@ -505,7 +505,7 @@ func TestMountGetParentPath(t *testing.T) { }, } - parentPath, err := mr.getMountPath(4, 44, 1) + parentPath, err := mr.getMountPath(4, 44, false) assert.NoError(t, err) assert.Equal(t, "/a/b/c", parentPath) } @@ -541,7 +541,7 @@ func TestMountLoop(t *testing.T) { }, } - parentPath, err := mr.getMountPath(3, 44, 1) + parentPath, err := mr.getMountPath(3, 44, false) assert.Equal(t, ErrMountLoop, err) assert.Equal(t, "", parentPath) } @@ -568,6 +568,6 @@ func BenchmarkGetParentPath(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, _ = mr.getMountPath(100, 44, 1) + _, _ = mr.getMountPath(100, 44, false) } } From 9e07eb9ac441354561ac389d6cac04f96ee9a664 Mon Sep 17 00:00:00 2001 From: Pierre Gimalac Date: Tue, 28 Nov 2023 10:22:31 +0100 Subject: [PATCH 45/87] Fix file handle check on Darwin (#21013) * refactor: rename freebsd file_handle check files to bsd * chore(file_handle): add package comment * feat(file_handles): fix bsd implementation for darwin * fix(file_handles): fix bsd tests * chore: add release note * fix: write proper oid name in logs and errors --- .../corechecks/system/filehandles/docs.go | 7 ++ .../system/filehandles/file_handles.go | 2 +- .../system/filehandles/file_handles_bsd.go | 71 +++++++++++++++++++ .../filehandles/file_handles_bsd_test.go | 45 ++++++++++++ .../system/filehandles/file_handles_darwin.go | 8 +++ .../filehandles/file_handles_freebsd.go | 65 +---------------- .../filehandles/file_handles_freebsd_test.go | 39 ---------- .../system/filehandles/file_handles_test.go | 2 +- ...file-check-on-darwin-ea525cc9369307a4.yaml | 11 +++ 9 files changed, 145 insertions(+), 105 deletions(-) create mode 100644 pkg/collector/corechecks/system/filehandles/docs.go create mode 100644 pkg/collector/corechecks/system/filehandles/file_handles_bsd.go create mode 100644 pkg/collector/corechecks/system/filehandles/file_handles_bsd_test.go create mode 100644 pkg/collector/corechecks/system/filehandles/file_handles_darwin.go delete mode 100644 pkg/collector/corechecks/system/filehandles/file_handles_freebsd_test.go create mode 100644 releasenotes/notes/file-check-on-darwin-ea525cc9369307a4.yaml diff --git a/pkg/collector/corechecks/system/filehandles/docs.go b/pkg/collector/corechecks/system/filehandles/docs.go new file mode 100644 index 0000000000000..add0f8e747c61 --- /dev/null +++ b/pkg/collector/corechecks/system/filehandles/docs.go @@ -0,0 +1,7 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package filehandles defines the file_handle core check +package filehandles diff --git a/pkg/collector/corechecks/system/filehandles/file_handles.go b/pkg/collector/corechecks/system/filehandles/file_handles.go index c7a31168094db..10703d355e5f6 100644 --- a/pkg/collector/corechecks/system/filehandles/file_handles.go +++ b/pkg/collector/corechecks/system/filehandles/file_handles.go @@ -2,7 +2,7 @@ // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build !windows && !freebsd +//go:build !windows && !freebsd && !darwin package filehandles diff --git a/pkg/collector/corechecks/system/filehandles/file_handles_bsd.go b/pkg/collector/corechecks/system/filehandles/file_handles_bsd.go new file mode 100644 index 0000000000000..2cd04b1907de9 --- /dev/null +++ b/pkg/collector/corechecks/system/filehandles/file_handles_bsd.go @@ -0,0 +1,71 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. +//go:build freebsd || darwin + +package filehandles + +import ( + "github.com/blabber/go-freebsd-sysctl/sysctl" + + "github.com/DataDog/datadog-agent/pkg/aggregator/sender" + "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" + "github.com/DataDog/datadog-agent/pkg/collector/check" + core "github.com/DataDog/datadog-agent/pkg/collector/corechecks" + "github.com/DataDog/datadog-agent/pkg/util/log" +) + +// For testing purpose +var getInt64 = sysctl.GetInt64 + +const fileHandlesCheckName = "file_handle" + +type fhCheck struct { + core.CheckBase +} + +// Run executes the check +func (c *fhCheck) Run() error { + + sender, err := c.GetSender() + if err != nil { + return err + } + openFh, err := getInt64(openfilesOID) + if err != nil { + log.Warnf("Error getting %s value %v", openfilesOID, err) + return err + } + maxFh, err := getInt64("kern.maxfiles") + if err != nil { + log.Warnf("Error getting kern.maxfiles value %v", err) + return err + } + log.Debugf("Submitting %s %v", openfilesOID, openFh) + log.Debugf("Submitting kern.maxfiles %v", maxFh) + sender.Gauge("system.fs.file_handles.used", float64(openFh), "", nil) + sender.Gauge("system.fs.file_handles.max", float64(maxFh), "", nil) + sender.Commit() + + return nil +} + +// The check doesn't need configuration +func (c *fhCheck) Configure(senderManager sender.SenderManager, integrationConfigDigest uint64, data integration.Data, initConfig integration.Data, source string) (err error) { + if err := c.CommonConfigure(senderManager, integrationConfigDigest, initConfig, data, source); err != nil { + return err + } + + return err +} + +func fhFactory() check.Check { + return &fhCheck{ + CheckBase: core.NewCheckBase(fileHandlesCheckName), + } +} + +func init() { + core.RegisterCheck(fileHandlesCheckName, fhFactory) +} diff --git a/pkg/collector/corechecks/system/filehandles/file_handles_bsd_test.go b/pkg/collector/corechecks/system/filehandles/file_handles_bsd_test.go new file mode 100644 index 0000000000000..55c168d11e461 --- /dev/null +++ b/pkg/collector/corechecks/system/filehandles/file_handles_bsd_test.go @@ -0,0 +1,45 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. +//go:build freebsd || darwin + +package filehandles + +import ( + "testing" + + "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" + "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" +) + +func GetInt64(_ string) (value int64, err error) { + value = 65534 + err = nil + return +} + +func TestFhCheckFreeBSD(t *testing.T) { + getInt64 = GetInt64 + + // we have to init the mocked sender here before fileHandleCheck.Configure(mock.GetSenderManager(), integration.FakeConfigHash, ...) + // (and append it to the aggregator, which is automatically done in NewMockSender) + // because the FinalizeCheckServiceTag is called in Configure. + // Hopefully, the check ID is an empty string while running unit tests; + mock := mocksender.NewMockSender("") + + fileHandleCheck := new(fhCheck) + fileHandleCheck.Configure(mock.GetSenderManager(), integration.FakeConfigHash, nil, nil, "test") + + // reset the check ID for the sake of correctness + mocksender.SetSender(mock, fileHandleCheck.ID()) + + mock.On("Gauge", "system.fs.file_handles.used", float64(65534), "", []string(nil)).Return().Times(1) + mock.On("Gauge", "system.fs.file_handles.max", float64(65534), "", []string(nil)).Return().Times(1) + mock.On("Commit").Return().Times(1) + fileHandleCheck.Run() + + mock.AssertExpectations(t) + mock.AssertNumberOfCalls(t, "Gauge", 2) + mock.AssertNumberOfCalls(t, "Commit", 1) +} diff --git a/pkg/collector/corechecks/system/filehandles/file_handles_darwin.go b/pkg/collector/corechecks/system/filehandles/file_handles_darwin.go new file mode 100644 index 0000000000000..fba9b26115cd4 --- /dev/null +++ b/pkg/collector/corechecks/system/filehandles/file_handles_darwin.go @@ -0,0 +1,8 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package filehandles + +const openfilesOID = "kern.num_files" diff --git a/pkg/collector/corechecks/system/filehandles/file_handles_freebsd.go b/pkg/collector/corechecks/system/filehandles/file_handles_freebsd.go index 3dabce43e9d17..9818f86001e78 100644 --- a/pkg/collector/corechecks/system/filehandles/file_handles_freebsd.go +++ b/pkg/collector/corechecks/system/filehandles/file_handles_freebsd.go @@ -2,70 +2,7 @@ // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build freebsd package filehandles -import ( - "github.com/blabber/go-freebsd-sysctl/sysctl" - - "github.com/DataDog/datadog-agent/pkg/aggregator/sender" - "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" - "github.com/DataDog/datadog-agent/pkg/collector/check" - core "github.com/DataDog/datadog-agent/pkg/collector/corechecks" - "github.com/DataDog/datadog-agent/pkg/util/log" -) - -// For testing purpose -var getInt64 = sysctl.GetInt64 - -const fileHandlesCheckName = "file_handle" - -type fhCheck struct { - core.CheckBase -} - -// Run executes the check -func (c *fhCheck) Run() error { - - sender, err := c.GetSender() - if err != nil { - return err - } - openFh, err := getInt64("kern.openfiles") - if err != nil { - log.Warnf("Error getting kern.openfiles value %v", err) - return err - } - maxFh, err := getInt64("kern.maxfiles") - if err != nil { - log.Warnf("Error getting kern.maxfiles value %v", err) - return err - } - log.Debugf("Submitting kern.openfiles %v", openFh) - log.Debugf("Submitting kern.maxfiles %v", maxFh) - sender.Gauge("system.fs.file_handles.used", float64(openFh), "", nil) - sender.Gauge("system.fs.file_handles.max", float64(maxFh), "", nil) - sender.Commit() - - return nil -} - -// The check doesn't need configuration -func (c *fhCheck) Configure(senderManager sender.SenderManager, integrationConfigDigest uint64, data integration.Data, initConfig integration.Data, source string) (err error) { - if err := c.CommonConfigure(senderManager, integrationConfigDigest, initConfig, data, source); err != nil { - return err - } - - return err -} - -func fhFactory() check.Check { - return &fhCheck{ - CheckBase: core.NewCheckBase(fileHandlesCheckName), - } -} - -func init() { - core.RegisterCheck(fileHandlesCheckName, fhFactory) -} +const openfilesOID = "kern.openfiles" diff --git a/pkg/collector/corechecks/system/filehandles/file_handles_freebsd_test.go b/pkg/collector/corechecks/system/filehandles/file_handles_freebsd_test.go deleted file mode 100644 index 04182e34b94f1..0000000000000 --- a/pkg/collector/corechecks/system/filehandles/file_handles_freebsd_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016-present Datadog, Inc. -//go:build freebsd - -package filehandles - -import ( - "testing" - - "github.com/DataDog/datadog-agent/pkg/aggregator" - "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" - "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" -) - -func GetInt64(name string) (value int64, err error) { - value = 65534 - err = nil - return -} - -func TestFhCheckFreeBSD(t *testing.T) { - getInt64 = GetInt64 - - fileHandleCheck := new(fhCheck) - fileHandleCheck.Configure(aggregator.NewNoOpSenderManager(), integration.FakeConfigHash, nil, nil, "test") - - mock := mocksender.NewMockSender(fileHandleCheck.ID()) - - mock.On("Gauge", "system.fs.file_handles.used", 421, "", []string(nil)).Return().Times(1) - mock.On("Gauge", "system.fs.file_handles.max", 65534, "", []string(nil)).Return().Times(1) - mock.On("Commit").Return().Times(1) - fileHandleCheck.Run() - - mock.AssertExpectations(t) - mock.AssertNumberOfCalls(t, "Gauge", 2) - mock.AssertNumberOfCalls(t, "Commit", 1) -} diff --git a/pkg/collector/corechecks/system/filehandles/file_handles_test.go b/pkg/collector/corechecks/system/filehandles/file_handles_test.go index 4db2d94133fdf..ab8dd2fa57935 100644 --- a/pkg/collector/corechecks/system/filehandles/file_handles_test.go +++ b/pkg/collector/corechecks/system/filehandles/file_handles_test.go @@ -2,7 +2,7 @@ // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build !windows +//go:build !windows && !freebsd && !darwin package filehandles diff --git a/releasenotes/notes/file-check-on-darwin-ea525cc9369307a4.yaml b/releasenotes/notes/file-check-on-darwin-ea525cc9369307a4.yaml new file mode 100644 index 0000000000000..c0a8ca03a195c --- /dev/null +++ b/releasenotes/notes/file-check-on-darwin-ea525cc9369307a4.yaml @@ -0,0 +1,11 @@ +# Each section from every release note are combined when the +# CHANGELOG.rst is rendered. So the text needs to be worded so that +# it does not depend on any information only available in another +# section. This may mean repeating some details, but each section +# must be readable independently of the other. +# +# Each section note must be formatted as reStructuredText. +--- +fixes: + - | + Fix `file_handle` core check on Darwin by using `sysctl` system call. From 7b213b014cd1f1a1a6149ee666d8d04b22350f6a Mon Sep 17 00:00:00 2001 From: Yoann Ghigoff Date: Tue, 28 Nov 2023 11:28:35 +0100 Subject: [PATCH 46/87] [CWS] dentry_resolver: check depth instead of loop counter when looking for discarded dentry (#20821) * check dentry depth instead loop counter to check for discarders * pass 4.14 verifier --- pkg/security/ebpf/c/include/hooks/dentry_resolver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/security/ebpf/c/include/hooks/dentry_resolver.h b/pkg/security/ebpf/c/include/hooks/dentry_resolver.h index 866f241e41818..ea52c28a978d2 100644 --- a/pkg/security/ebpf/c/include/hooks/dentry_resolver.h +++ b/pkg/security/ebpf/c/include/hooks/dentry_resolver.h @@ -41,7 +41,7 @@ int __attribute__((always_inline)) resolve_dentry_tail_call(void *ctx, struct de next_key.mount_id = 0; } - if (input->discarder_type && i <= 3) { + if (input->discarder_type && input->iteration == 1 && i <= 3) { params->discarder.path_key.ino = key.ino; params->discarder.path_key.mount_id = key.mount_id; params->discarder.is_leaf = i == 0; From cd7712c492c24d6bcbfd2770e23bb867df8b75ff Mon Sep 17 00:00:00 2001 From: Guy Arbitman Date: Tue, 28 Nov 2023 12:33:38 +0200 Subject: [PATCH 47/87] usm: tests: Make request slower for protocol classification (#21100) * usm: tests: Make request slower for protocol classification * usm: tests: Make request slower for protocol classification --- pkg/network/tracer/testutil/grpc/server.go | 18 ++++++-- pkg/network/tracer/tracer_usm_linux_test.go | 49 +++++++++------------ 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/pkg/network/tracer/testutil/grpc/server.go b/pkg/network/tracer/testutil/grpc/server.go index dae23aa6eec62..94e65a75f829b 100644 --- a/pkg/network/tracer/testutil/grpc/server.go +++ b/pkg/network/tracer/testutil/grpc/server.go @@ -226,10 +226,17 @@ func NewServer(addr string) (*Server, error) { return nil, err } + server := NewServerWithoutBind() + server.lis = lis + server.Address = lis.Addr().String() + + return server, nil +} + +// NewServerWithoutBind returns a new instance of the gRPC server. +func NewServerWithoutBind() *Server { server := &Server{ - Address: lis.Addr().String(), grpcSrv: grpc.NewServer(grpc.MaxRecvMsgSize(100*1024*1024), grpc.MaxSendMsgSize(100*1024*1024)), - lis: lis, routeNotes: make(map[string][]*routeguide.RouteNote), } @@ -238,7 +245,12 @@ func NewServer(addr string) (*Server, error) { routeguide.RegisterRouteGuideServer(server.grpcSrv, server) pbStream.RegisterMathServer(server.grpcSrv, server) - return server, nil + return server +} + +// GetGRPCServer returns the gRPC server. +func (s *Server) GetGRPCServer() *grpc.Server { + return s.grpcSrv } func (s *Server) Stop() { diff --git a/pkg/network/tracer/tracer_usm_linux_test.go b/pkg/network/tracer/tracer_usm_linux_test.go index 7b31b6b03db69..5807ef53961de 100644 --- a/pkg/network/tracer/tracer_usm_linux_test.go +++ b/pkg/network/tracer/tracer_usm_linux_test.go @@ -8,7 +8,6 @@ package tracer import ( - "bufio" "bytes" "context" "crypto/tls" @@ -27,6 +26,7 @@ import ( "testing" "time" + gorilla "github.com/gorilla/mux" krpretty "github.com/kr/pretty" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -51,6 +51,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/network/tracer/connection/kprobe" "github.com/DataDog/datadog-agent/pkg/network/tracer/testutil/grpc" "github.com/DataDog/datadog-agent/pkg/network/usm/utils" + grpc2 "github.com/DataDog/datadog-agent/pkg/util/grpc" ) func httpSupported() bool { @@ -598,9 +599,6 @@ func (s *USMSuite) TestProtocolClassification() { func testProtocolConnectionProtocolMapCleanup(t *testing.T, tr *Tracer, clientHost, targetHost, serverHost string) { t.Run("protocol cleanup", func(t *testing.T) { - if tr.ebpfTracer.Type() == connection.TracerTypeFentry { - t.Skip("protocol classification not supported for fentry tracer") - } t.Cleanup(func() { tr.ebpfTracer.Pause() }) dialer := &net.Dialer{ @@ -624,17 +622,21 @@ func testProtocolConnectionProtocolMapCleanup(t *testing.T, tr *Tracer, clientHo initTracerState(t, tr) require.NoError(t, tr.ebpfTracer.Resume()) - HTTPServer := NewTCPServerOnAddress(serverHost, func(c net.Conn) { - r := bufio.NewReader(c) - input, err := r.ReadBytes(byte('\n')) - if err == nil { - c.Write(input) - } - c.Close() + mux := gorilla.NewRouter() + mux.Handle("/test", nethttp.DefaultServeMux) + grpcHandler := grpc.NewServerWithoutBind() + + lis, err := net.Listen("tcp", serverHost) + require.NoError(t, err) + srv := grpc2.NewMuxedGRPCServer(serverHost, nil, grpcHandler.GetGRPCServer(), mux) + srv.Addr = lis.Addr().String() + + go srv.Serve(lis) + t.Cleanup(func() { + _ = srv.Shutdown(context.Background()) + _ = lis.Close() }) - t.Cleanup(HTTPServer.Shutdown) - require.NoError(t, HTTPServer.Run()) - _, port, err := net.SplitHostPort(HTTPServer.address) + _, port, err := net.SplitHostPort(srv.Addr) require.NoError(t, err) targetAddr := net.JoinHostPort(targetHost, port) @@ -644,19 +646,13 @@ func testProtocolConnectionProtocolMapCleanup(t *testing.T, tr *Tracer, clientHo DialContext: dialer.DialContext, }, } - resp, err := client.Get("http://" + targetAddr + "/test") - if err == nil { - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - } + resp, err := client.Post("http://"+targetAddr+"/test", "text/plain", bytes.NewReader(bytes.Repeat([]byte("test"), 100))) + require.NoError(t, err) + io.Copy(io.Discard, resp.Body) + resp.Body.Close() client.CloseIdleConnections() - waitForConnectionsWithProtocol(t, tr, targetAddr, HTTPServer.address, &protocols.Stack{Application: protocols.HTTP}) - HTTPServer.Shutdown() - - gRPCServer, err := grpc.NewServer(HTTPServer.address) - require.NoError(t, err) - gRPCServer.Run() + waitForConnectionsWithProtocol(t, tr, targetAddr, srv.Addr, &protocols.Stack{Application: protocols.HTTP}) grpcClient, err := grpc.NewClient(targetAddr, grpc.Options{ CustomDialer: dialer, @@ -664,8 +660,7 @@ func testProtocolConnectionProtocolMapCleanup(t *testing.T, tr *Tracer, clientHo require.NoError(t, err) defer grpcClient.Close() _ = grpcClient.HandleUnary(context.Background(), "test") - gRPCServer.Stop() - waitForConnectionsWithProtocol(t, tr, targetAddr, gRPCServer.Address, &protocols.Stack{Api: protocols.GRPC, Application: protocols.HTTP2}) + waitForConnectionsWithProtocol(t, tr, targetAddr, srv.Addr, &protocols.Stack{Api: protocols.GRPC, Application: protocols.HTTP2}) }) } From 583067e6f6855876a33a37eb6ba580e8c55c84e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:55:24 +0100 Subject: [PATCH 48/87] Bump golang.org/x/sys from 0.13.0 to 0.15.0 in /pkg/gohai (#21138) * Bump golang.org/x/sys from 0.13.0 to 0.15.0 in /pkg/gohai Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.13.0 to 0.15.0. - [Commits](https://github.com/golang/sys/compare/v0.13.0...v0.15.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Auto-generate go.sum and LICENSE-3rdparty.csv changes --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] --- pkg/gohai/go.mod | 2 +- pkg/gohai/go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/gohai/go.mod b/pkg/gohai/go.mod index f2dde927a9581..335e0dc5c5543 100644 --- a/pkg/gohai/go.mod +++ b/pkg/gohai/go.mod @@ -10,7 +10,7 @@ require ( github.com/moby/sys/mountinfo v0.7.1 github.com/shirou/gopsutil/v3 v3.23.10 github.com/stretchr/testify v1.8.4 - golang.org/x/sys v0.13.0 + golang.org/x/sys v0.15.0 ) require ( diff --git a/pkg/gohai/go.sum b/pkg/gohai/go.sum index ee59609ae21dd..1a1044c839089 100644 --- a/pkg/gohai/go.sum +++ b/pkg/gohai/go.sum @@ -57,8 +57,9 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From 6b453bf2db7b1e3ee2ed22891fb76931687c55ec Mon Sep 17 00:00:00 2001 From: Pierre Gimalac Date: Tue, 28 Nov 2023 13:45:49 +0100 Subject: [PATCH 49/87] fix: remove superfluous http error (#21102) --- cmd/agent/api/security.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/agent/api/security.go b/cmd/agent/api/security.go index d265959ac0dd5..4d30af54912a4 100644 --- a/cmd/agent/api/security.go +++ b/cmd/agent/api/security.go @@ -27,7 +27,6 @@ var ( func validateToken(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := util.Validate(w, r); err != nil { - http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) return } next.ServeHTTP(w, r) From 570bc89e67467fb3e55ae8cc215540420ea18eb9 Mon Sep 17 00:00:00 2001 From: Alex Lopez Date: Tue, 28 Nov 2023 14:18:29 +0100 Subject: [PATCH 50/87] Add a constraints file and constrain bcrypt (#21147) --- .../software/datadog-agent-integrations-py3.rb | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/omnibus/config/software/datadog-agent-integrations-py3.rb b/omnibus/config/software/datadog-agent-integrations-py3.rb index 48bda18943bcc..ffb437079370d 100644 --- a/omnibus/config/software/datadog-agent-integrations-py3.rb +++ b/omnibus/config/software/datadog-agent-integrations-py3.rb @@ -263,6 +263,18 @@ vars: { requirements: lib_req["req_lines"] } end + # Constraints file for constraining transitive dependencies in those cases where there may be incompatible versions + constraints = [] + if redhat_target? + constraints.push("bcrypt < 4.1.0") + end + + constraints_file = windows_safe_path(project_dir, "constraints.txt") + block "Write constraints file" do + File.open(constraints_file, 'w') { |f| f << constraints.join("\n") } + end + + # Increasing pip max retries (default: 5 times) and pip timeout (default 15 seconds) to avoid blocking network errors pip_max_retries = 20 pip_timeout = 20 @@ -273,11 +285,11 @@ command "#{python} -m pip install datadog_checks_base --no-deps --no-index --find-links=#{wheel_build_dir}" command "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => build_env, :cwd => cwd_downloader command "#{python} -m pip install datadog_checks_downloader --no-deps --no-index --find-links=#{wheel_build_dir}" - command "#{python} -m piptools compile --generate-hashes --output-file #{compiled_reqs_file_path} #{static_reqs_out_file} " \ + command "#{python} -m piptools compile --generate-hashes -c #{constraints_file} --output-file #{compiled_reqs_file_path} #{static_reqs_out_file} " \ "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => build_env # Pip-compiling seperately each lib that needs a custom build installation specific_build_env.each do |lib, env| - command "#{python} -m piptools compile --generate-hashes --output-file #{requirements_custom[lib]["compiled_req_file_path"]} #{requirements_custom[lib]["req_file_path"]} " \ + command "#{python} -m piptools compile --generate-hashes -c #{constraints_file} --output-file #{requirements_custom[lib]["compiled_req_file_path"]} #{requirements_custom[lib]["req_file_path"]} " \ "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => env end From d5b990d7eb1d1a07712df7a51d0a1c4b2acb828f Mon Sep 17 00:00:00 2001 From: Guillaume Pagnoux Date: Tue, 28 Nov 2023 14:54:14 +0100 Subject: [PATCH 51/87] [USM] http2: add documentation and clearer names (#21006) * [USM] http2: add documentation and clearer names Signed-off-by: Guillaume Pagnoux * Fix index doc Co-authored-by: Guy Arbitman * fix CR Signed-off-by: Guillaume Pagnoux * Update pkg/network/ebpf/c/protocols/http2/decoding.h Co-authored-by: Guy Arbitman --------- Signed-off-by: Guillaume Pagnoux Co-authored-by: Guy Arbitman --- pkg/network/ebpf/c/protocols/http2/decoding.h | 104 ++++++++++++------ 1 file changed, 73 insertions(+), 31 deletions(-) diff --git a/pkg/network/ebpf/c/protocols/http2/decoding.h b/pkg/network/ebpf/c/protocols/http2/decoding.h index 7a7aae8b91393..5ee59bf0fd0bd 100644 --- a/pkg/network/ebpf/c/protocols/http2/decoding.h +++ b/pkg/network/ebpf/c/protocols/http2/decoding.h @@ -42,17 +42,26 @@ static __always_inline http2_stream_t *http2_fetch_stream(const http2_stream_key return bpf_map_lookup_elem(&http2_in_flight, http2_stream_key); } -// Similar to read_var_int, but with a small optimization of getting the current character as input argument. -static __always_inline bool read_var_int_with_given_current_char(struct __sk_buff *skb, skb_info_t *skb_info, __u64 current_char_as_number, __u64 max_number_for_bits, __u64 *out) { +// Similar to read_hpack_int, but with a small optimization of getting the +// current character as input argument. +static __always_inline bool read_hpack_int_with_given_current_char(struct __sk_buff *skb, skb_info_t *skb_info, __u64 current_char_as_number, __u64 max_number_for_bits, __u64 *out) { current_char_as_number &= max_number_for_bits; + // In HPACK, if the number is too big to be stored in max_number_for_bits + // bits, then those bits are all set to one, and the rest of the number must + // be read from subsequent bytes. if (current_char_as_number < max_number_for_bits) { *out = current_char_as_number; return true; } + // Read the next byte, and check if it is the last byte of the number. + // While HPACK does support arbitrary sized numbers, we are limited by the + // number of instructions we can use in a single eBPF program, so we only + // parse one additional byte. The max value that can be parsed is + // `(2^max_number_for_bits - 1) + 127`. __u64 next_char = 0; - if (bpf_skb_load_bytes(skb, skb_info->data_off, &next_char, 1) >=0 && (next_char & 128) == 0) { + if (bpf_skb_load_bytes(skb, skb_info->data_off, &next_char, 1) >= 0 && (next_char & 128) == 0) { skb_info->data_off++; *out = current_char_as_number + (next_char & 127); return true; @@ -61,35 +70,42 @@ static __always_inline bool read_var_int_with_given_current_char(struct __sk_buf return false; } -// read_var_int reads an unsigned variable length integer off the -// beginning of p. n is the parameter as described in -// https://httpwg.org/specs/rfc7541.html#rfc.section.5.1. +// read_hpack_int reads an unsigned variable length integer as specified in the +// HPACK specification, from an skb. // -// n must always be between 1 and 8. +// See https://httpwg.org/specs/rfc7541.html#rfc.section.5.1 for more details on +// how numbers are represented in HPACK. // -// The returned remain buffer is either a smaller suffix of p, or err != nil. -static __always_inline bool read_var_int(struct __sk_buff *skb, skb_info_t *skb_info, __u64 max_number_for_bits, __u64 *out) { +// max_number_for_bits represents the number of bits in the first byte that are +// used to represent the MSB of number. It must always be between 1 and 8. +// +// The parsed number is stored in out. +// +// read_hpack_int returns true if the integer was successfully parsed, and false +// otherwise. +static __always_inline bool read_hpack_int(struct __sk_buff *skb, skb_info_t *skb_info, __u64 max_number_for_bits, __u64 *out) { __u64 current_char_as_number = 0; if (bpf_skb_load_bytes(skb, skb_info->data_off, ¤t_char_as_number, 1) < 0) { return false; } skb_info->data_off++; - return read_var_int_with_given_current_char(skb, skb_info, current_char_as_number, max_number_for_bits, out); + return read_hpack_int_with_given_current_char(skb, skb_info, current_char_as_number, max_number_for_bits, out); } -//get_dynamic_counter returns the current dynamic counter by the conn tup. +// get_dynamic_counter returns the current dynamic counter by the conn tuple. static __always_inline __u64 *get_dynamic_counter(conn_tuple_t *tup) { __u64 counter = 0; bpf_map_update_elem(&http2_dynamic_counter_table, tup, &counter, BPF_NOEXIST); return bpf_map_lookup_elem(&http2_dynamic_counter_table, tup); } -// parse_field_indexed is handling the case which the header frame is part of the static table. +// parse_field_indexed parses fully-indexed headers. static __always_inline void parse_field_indexed(dynamic_table_index_t *dynamic_index, http2_header_t *headers_to_process, __u8 index, __u64 global_dynamic_counter, __u8 *interesting_headers_counter) { if (headers_to_process == NULL) { return; } + // TODO: can improve by declaring MAX_INTERESTING_STATIC_TABLE_INDEX if (is_interesting_static_entry(index)) { headers_to_process->index = index; @@ -101,8 +117,8 @@ static __always_inline void parse_field_indexed(dynamic_table_index_t *dynamic_i return; } - // we change the index to fit our internal dynamic table implementation index. - // the index is starting from 1 so we decrease 62 in order to be equal to the given index. + // We change the index to match our internal dynamic table implementation index. + // Our internal indexes start from 1, so we subtract 61 in order to match the given index. dynamic_index->index = global_dynamic_counter - (index - MAX_STATIC_TABLE_INDEX); if (bpf_map_lookup_elem(&http2_dynamic_table, dynamic_index) == NULL) { @@ -117,29 +133,35 @@ static __always_inline void parse_field_indexed(dynamic_table_index_t *dynamic_i READ_INTO_BUFFER(path, HTTP2_MAX_PATH_LEN, BLK_SIZE) -// parse_field_literal handling the case when the key is part of the static table and the value is a dynamic string -// which will be stored in the dynamic table. +// parse_field_literal parses a header with a literal value. +// +// We are only interested in path headers, that we will store in our internal +// dynamic table, and will skip headers that are not path headers. static __always_inline bool parse_field_literal(struct __sk_buff *skb, skb_info_t *skb_info, http2_header_t *headers_to_process, __u64 index, __u64 global_dynamic_counter, __u8 *interesting_headers_counter) { __u64 str_len = 0; - if (!read_var_int(skb, skb_info, MAX_6_BITS, &str_len)) { + if (!read_hpack_int(skb, skb_info, MAX_6_BITS, &str_len)) { return false; } - // The key is new and inserted into the dynamic table. So we are skipping the new value. + // The header name is new and inserted in the dynamic table - we skip the new value. if (index == 0) { skb_info->data_off += str_len; str_len = 0; - if (!read_var_int(skb, skb_info, MAX_6_BITS, &str_len)) { + if (!read_hpack_int(skb, skb_info, MAX_6_BITS, &str_len)) { return false; } goto end; } + + // We skip if: + // - The string is too big + // - This is not a path + // - We won't be able to store the header info if (str_len > HTTP2_MAX_PATH_LEN || index != kIndexPath || headers_to_process == NULL) { goto end; } - __u32 final_size = str_len < HTTP2_MAX_PATH_LEN ? str_len : HTTP2_MAX_PATH_LEN; - if (skb_info->data_off + final_size > skb_info->data_end) { + if (skb_info->data_off + str_len > skb_info->data_end) { goto end; } @@ -148,12 +170,16 @@ static __always_inline bool parse_field_literal(struct __sk_buff *skb, skb_info_ headers_to_process->new_dynamic_value_offset = skb_info->data_off; headers_to_process->new_dynamic_value_size = str_len; (*interesting_headers_counter)++; + end: skb_info->data_off += str_len; return true; } -// This function reads the http2 headers frame. +// filter_relevant_headers parses the http2 headers frame, and filters headers +// that are relevant for us, to be processed later on. +// The return value is the number of relevant headers that were found and inserted +// in the `headers_to_process` table. static __always_inline __u8 filter_relevant_headers(struct __sk_buff *skb, skb_info_t *skb_info, conn_tuple_t *tup, dynamic_table_index_t *dynamic_index, http2_header_t *headers_to_process, __u32 frame_length) { __u8 current_ch; __u8 interesting_headers = 0; @@ -190,7 +216,7 @@ static __always_inline __u8 filter_relevant_headers(struct __sk_buff *skb, skb_i } index = 0; - if (!read_var_int_with_given_current_char(skb, skb_info, current_ch, max_bits, &index)) { + if (!read_hpack_int_with_given_current_char(skb, skb_info, current_ch, max_bits, &index)) { break; } @@ -218,6 +244,8 @@ static __always_inline __u8 filter_relevant_headers(struct __sk_buff *skb, skb_i return interesting_headers; } +// process_headers processes the headers that were filtered in filter_relevant_headers, +// looking for requests path, status code, and method. static __always_inline void process_headers(struct __sk_buff *skb, dynamic_table_index_t *dynamic_index, http2_stream_t *current_stream, http2_header_t *headers_to_process, __u8 interesting_headers) { http2_header_t *current_header; dynamic_table_entry_t dynamic_value = {}; @@ -273,6 +301,7 @@ static __always_inline void process_headers(struct __sk_buff *skb, dynamic_table } static __always_inline void handle_end_of_stream(http2_stream_t *current_stream, http2_stream_key_t *http2_stream_key_template) { + // We want to see the EOS twice for a given stream: one for the client, one for the server. if (!current_stream->request_end_of_stream) { current_stream->request_end_of_stream = true; return; @@ -349,6 +378,8 @@ static __always_inline bool format_http2_frame_header(struct http2_frame *out) { return out->type <= kContinuationFrame && out->length <= MAX_FRAME_SIZE && (out->stream_id == 0 || (out->stream_id % 2 == 1)); } +// skip_preface is a helper function to check for the HTTP2 magic sent at the beginning +// of an HTTP2 connection, and skip it if present. static __always_inline void skip_preface(struct __sk_buff *skb, skb_info_t *skb_info) { char preface[HTTP2_MARKER_SIZE]; bpf_memset((char *)preface, 0, HTTP2_MARKER_SIZE); @@ -394,10 +425,7 @@ static __always_inline void fix_header_frame(struct __sk_buff *skb, skb_info_t * } static __always_inline void reset_frame(struct http2_frame *out) { - out->type = 0; - out->length = 0; - out->stream_id = 0; - out->flags = 0; + *out = (struct http2_frame){ 0 }; } static __always_inline bool get_first_frame(struct __sk_buff *skb, skb_info_t *skb_info, frame_header_remainder_t *frame_state, struct http2_frame *current_frame) { @@ -473,10 +501,24 @@ static __always_inline bool get_first_frame(struct __sk_buff *skb, skb_info_t *s return false; } -static __always_inline __u8 find_relevant_headers(struct __sk_buff *skb, skb_info_t *skb_info, http2_frame_with_offset *frames_array, __u8 original_index) { +// find_relevant_frames iterates over the packet and finds frames that are +// relevant for us. The frames info and location are stored in the `frames_array` array, +// and the number of frames found is returned. +// +// We consider frames as relevant if they are either: +// - HEADERS frames +// - RST_STREAM frames +// - DATA frames with the END_STREAM flag set +static __always_inline __u8 find_relevant_frames(struct __sk_buff *skb, skb_info_t *skb_info, http2_frame_with_offset *frames_array, __u8 original_index) { bool is_headers_or_rst_frame, is_data_end_of_stream; __u8 interesting_frame_index = 0; struct http2_frame current_frame = {}; + + // We may have found a relevant frame already in http2_handle_first_frame, + // so we need to adjust the index accordingly. We do not set + // interesting_frame_index to original_index directly, as this will confuse + // the verifier, leading it into thinking the index could have an arbitrary + // value. if (original_index == 1) { interesting_frame_index = 1; } @@ -550,7 +592,7 @@ int socket__http2_handle_first_frame(struct __sk_buff *skb) { iteration_value->frames_count = 0; iteration_value->iteration = 0; - // Filter preface. + // skip HTTP2 magic, if present skip_preface(skb, &dispatcher_args_copy.skb_info); frame_header_remainder_t *frame_state = bpf_map_lookup_elem(&http2_remainder, &dispatcher_args_copy.tup); @@ -607,9 +649,9 @@ int socket__http2_filter(struct __sk_buff *skb) { // The verifier cannot tell if `iteration_value->frames_count` is 0 or 1, so we have to help it. The value is // 1 if we have found an interesting frame in `socket__http2_handle_first_frame`, otherwise it is 0. // filter frames - iteration_value->frames_count = find_relevant_headers(skb, &local_skb_info, iteration_value->frames_array, iteration_value->frames_count); + iteration_value->frames_count = find_relevant_frames(skb, &local_skb_info, iteration_value->frames_array, iteration_value->frames_count); - frame_header_remainder_t new_frame_state = {0}; + frame_header_remainder_t new_frame_state = { 0 }; if (local_skb_info.data_off > local_skb_info.data_end) { // We have a remainder new_frame_state.remainder = local_skb_info.data_off - local_skb_info.data_end; From bdddd8150e573f2cae724a71be9ccf868240ac62 Mon Sep 17 00:00:00 2001 From: Usama Saqib Date: Tue, 28 Nov 2023 15:35:10 +0100 Subject: [PATCH 52/87] Change availability zone on EC2 state change timeout error (#21084) * change AZ on state change error * add comment --- test/new-e2e/system-probe/system-probe-test-env.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/new-e2e/system-probe/system-probe-test-env.go b/test/new-e2e/system-probe/system-probe-test-env.go index 0841428ba4d45..c3a7002d37d7b 100644 --- a/test/new-e2e/system-probe/system-probe-test-env.go +++ b/test/new-e2e/system-probe/system-probe-test-env.go @@ -228,7 +228,9 @@ func NewTestEnv(name, x86InstanceType, armInstanceType string, opts *SystemProbe }, opts.FailOnMissing) if err != nil { return handleScenarioFailure(err, func(possibleError handledError) { - if possibleError.errorType == insufficientCapacityError { + // handle the following errors by trying in a different availability zone + if possibleError.errorType == insufficientCapacityError || + possibleError.errorType == ec2StateChangeTimeoutError { currentAZ++ } }) From 3ae4587453c6c2bcf685bf2fa37e024563a3f69b Mon Sep 17 00:00:00 2001 From: Spencer Gilbert Date: Tue, 28 Nov 2023 10:55:17 -0500 Subject: [PATCH 53/87] chore: APL-2570 add a process-agent kitchen test to branch CI (#21127) chore: APL-2570 add a process-agent kitchen test to branch CI --- .gitlab/kitchen_testing/debian.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.gitlab/kitchen_testing/debian.yml b/.gitlab/kitchen_testing/debian.yml index 6d3b5203a80a3..6f0fff4d2bd14 100644 --- a/.gitlab/kitchen_testing/debian.yml +++ b/.gitlab/kitchen_testing/debian.yml @@ -5,7 +5,6 @@ # include: # - /.gitlab/kitchen_common/testing.yml - # Kitchen: OSes # ------------- @@ -91,16 +90,14 @@ kitchen_debian_step_by_step_agent-a6: - .kitchen_os_with_cws - .kitchen_scenario_debian_a6_x64 - .kitchen_test_step_by_step_agent - rules: - !reference [.on_deploy_a6] + rules: !reference [.on_deploy_a6] kitchen_debian_step_by_step_agent-a7: extends: - .kitchen_os_with_cws - .kitchen_scenario_debian_a7_x64 - .kitchen_test_step_by_step_agent - rules: - !reference [.on_deploy_a7] + rules: !reference [.on_deploy_a7] kitchen_debian_upgrade5_agent-a6: extends: @@ -136,11 +133,11 @@ kitchen_debian_upgrade7_iot_agent-a7: - .kitchen_test_upgrade7_iot_agent kitchen_debian_process_agent-a7: + rules: + - !reference [.on_default_kitchen_tests_a7] variables: KITCHEN_OSVERS: "debian-11" DEFAULT_KITCHEN_OSVERS: "debian-11" extends: - .kitchen_scenario_debian_a7_x64 - .kitchen_test_process_agent - - From 8bfeaac291651dca3226a94e04034185ffa8f8d3 Mon Sep 17 00:00:00 2001 From: Jules Macret <110237980+julesmcrt@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:05:48 +0100 Subject: [PATCH 54/87] [Fleet] Doc update for agent config sources (#20891) Update README.md Lower `inventories_min_interval` to 1min Add new fields to the inventories payload Remove cloud_provider and add info to Format section Co-authored-by: maxime mouial --- .../internal/util/inventory_payload.go | 2 +- comp/metadata/inventoryagent/README.md | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/comp/metadata/internal/util/inventory_payload.go b/comp/metadata/internal/util/inventory_payload.go index 1318c6feb87ef..877f7ca14515a 100644 --- a/comp/metadata/internal/util/inventory_payload.go +++ b/comp/metadata/internal/util/inventory_payload.go @@ -68,7 +68,7 @@ import ( ) var ( - defaultMinInterval = 5 * time.Minute + defaultMinInterval = 1 * time.Minute defaultMaxInterval = 10 * time.Minute // For testing purposes diff --git a/comp/metadata/inventoryagent/README.md b/comp/metadata/inventoryagent/README.md index 38842969f9e46..017997f8267a9 100644 --- a/comp/metadata/inventoryagent/README.md +++ b/comp/metadata/inventoryagent/README.md @@ -6,7 +6,7 @@ This package populates some of the agent-related fields in the `inventories` pro This is enabled by default but can be turned off using `inventories_enabled` config. The payload is sent every 10min (see `inventories_max_interval` in the config) or whenever it's updated with at most 1 -update every 5 minutes (see `inventories_min_interval`). +update every minute (see `inventories_min_interval`). # Content @@ -103,6 +103,16 @@ The payload is a JSON dict with the following fields string. This includes the settings configured by the user (throuh the configuration file, the environment or CLI), as well as any settings explicitly set by the agent (for example the number of workers is dynamically set by the agent itself based on the load). + - `file_configuration` - **string**: the Agent configuration specified by the configuration file (scrubbed), as a YAML string. + Only the settings written in the configuration file are included, and their value might not match what's applyed by the agent because they can be overriden by other sources. + - `environment_variable_configuration` - **string**: the Agent configuration specified by the environment variables (scrubbed), as a YAML string. + Only the settings written in the environment variables are included, and their value might not match what's applyed by the agent because they can be overriden by other sources. + - `agent_runtime_configuration` - **string**: the Agent configuration set by the agent itself (scrubbed), as a YAML string. + Only the settings set by the agent itself are included, and their value might not match what's applyed by the agent because they can be overriden by other sources. + - `remote_configuration` - **string**: the Agent configuration specified by the Remote Configuration (scrubbed), as a YAML string. + Only the settings currently used by Remote Configuration are included, and their value might not match what's applyed by the agent because they can be overriden by other sources. + - `cli_configuration` - **string**: the Agent configuration specified by the CLI (scrubbed), as a YAML string. + Only the settings set in the CLI are included, they cannot be overriden by any other sources. ("scrubbed" indicates that secrets are removed from the field value just as they are in logs) @@ -114,7 +124,6 @@ Here an example of an inventory payload: { "agent_metadata": { "agent_version": "7.37.0-devel+git.198.68a5b69", - "cloud_provider": "AWS", "config_apm_dd_url": "", "config_dd_url": "", "config_logs_dd_url": "", @@ -141,7 +150,11 @@ Here an example of an inventory payload: "install_method_tool_version": "", "logs_transport": "HTTP", "full_configuration": "", - "provided_configuration": "api_key: \"***************************aaaaa\"\ncheck_runners: 4\ncmd.check.fullsketches: false\ncontainerd_namespace: []\ncontainerd_namespaces: []\npython_version: \"3\"\ntracemalloc_debug: false" + "provided_configuration": "api_key: \"***************************aaaaa\"\ncheck_runners: 4\ncmd.check.fullsketches: false\ncontainerd_namespace: []\ncontainerd_namespaces: []\npython_version: \"3\"\ntracemalloc_debug: false\nlog_level: \"warn\"", + "file_configuration": "check_runners: 4\ncmd.check.fullsketches: false\ncontainerd_namespace: []\ncontainerd_namespaces: []\npython_version: \"3\"\ntracemalloc_debug: false", + "environment_variable_configuration": "api_key: \"***************************aaaaa\"", + "remote_configuration": "log_level: \"debug\"", + "cli_configuration": "log_level: \"warn\"" } "hostname": "my-host", "timestamp": 1631281754507358895 From 20086f2d8ff0404366f4a51f753ad7be0fe8b6fc Mon Sep 17 00:00:00 2001 From: Daniel Lavie Date: Tue, 28 Nov 2023 18:46:33 +0200 Subject: [PATCH 55/87] Added logSize for the CORE assert, so it won't get truncated (#21126) --- pkg/ebpf/co_re.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/ebpf/co_re.go b/pkg/ebpf/co_re.go index 6901ec0c179da..95cef42d7db89 100644 --- a/pkg/ebpf/co_re.go +++ b/pkg/ebpf/co_re.go @@ -61,6 +61,7 @@ func (c *coreAssetLoader) loadCOREAsset(filename string, startFn func(bytecode.A VerifierOptions: bpflib.CollectionOptions{ Programs: bpflib.ProgramOptions{ KernelTypes: btfData, + LogSize: 10 * 1024 * 1024, }, }, } From eb2515161be921059b0655ed2ea5330d5eb0d184 Mon Sep 17 00:00:00 2001 From: Spencer Gilbert Date: Tue, 28 Nov 2023 12:02:37 -0500 Subject: [PATCH 56/87] chore: Increase speculative_max_depth to 3 for MergeQueue (#21152) chore: Increase speculative_max_depth to 3 for MergeQueue --- repository.datadog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repository.datadog.yml b/repository.datadog.yml index 4b26ee0d56d4a..d029388b323a6 100644 --- a/repository.datadog.yml +++ b/repository.datadog.yml @@ -4,6 +4,6 @@ kind: mergequeue enable: true merge_method: squash workflow_type: speculative -speculative_max_depth: 1 +speculative_max_depth: 3 wait_for_check_timeout_in_minutes: 240 gitlab_jobs_retry_enable: true From 54b9fe26871957e738d7ff139c58c504b56c5778 Mon Sep 17 00:00:00 2001 From: "Brian L. Troutwine" Date: Tue, 28 Nov 2023 09:35:00 -0800 Subject: [PATCH 57/87] Adjust fuzz names to fix `invoke fuzz` break on Linux (#21021) The go fuzz requires that the regex given matches only a single function. Previously FuzzNormalizeTag as a prefix of FuzzNormalizeTagValue, breaking invoke fuzz when run on the trace/traceutil package on a Linux system. This did not trigger on OS X. Signed-off-by: Brian L. Troutwine --------- Signed-off-by: Brian L. Troutwine --- tasks/fuzz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/fuzz.py b/tasks/fuzz.py index 9f8f023f309d8..3f7b437890ffe 100644 --- a/tasks/fuzz.py +++ b/tasks/fuzz.py @@ -15,7 +15,7 @@ def fuzz(ctx, fuzztime="10s"): """ for directory, func in search_fuzz_tests(os.getcwd()): with ctx.cd(directory): - cmd = f'go test -v . -run={func} -fuzz={func} -fuzztime={fuzztime}' + cmd = f'go test -v . -run={func} -fuzz={func}$ -fuzztime={fuzztime}' ctx.run(cmd) From 7d31586939f0c3e186887bf00c322216d033d470 Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Tue, 28 Nov 2023 19:04:49 +0100 Subject: [PATCH 58/87] [CWS] make windows probe a bit more similar to the linux one (#21154) --- pkg/security/probe/probe_windows.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/security/probe/probe_windows.go b/pkg/security/probe/probe_windows.go index 8286b8355b376..123b77d9787a1 100644 --- a/pkg/security/probe/probe_windows.go +++ b/pkg/security/probe/probe_windows.go @@ -175,8 +175,6 @@ func (p *WindowsProbe) SendStats() error { // NewWindowsProbe instantiates a new runtime security agent probe func NewWindowsProbe(probe *Probe, config *config.Config, opts Opts) (*WindowsProbe, error) { - opts.normalize() - ctx, cancelFnc := context.WithCancel(context.Background()) p := &WindowsProbe{ @@ -190,8 +188,6 @@ func NewWindowsProbe(probe *Probe, config *config.Config, opts Opts) (*WindowsPr onStop: make(chan *procmon.ProcessStopNotification), } - probe.scrubber = newProcScrubber(config.Probe.CustomSensitiveWords) - var err error p.Resolvers, err = resolvers.NewResolvers(config, p.statsdClient, probe.scrubber) if err != nil { @@ -261,6 +257,7 @@ func NewProbe(config *config.Config, opts Opts) (*Probe, error) { Opts: opts, Config: config, StatsdClient: opts.StatsdClient, + scrubber: newProcScrubber(config.Probe.CustomSensitiveWords), } pp, err := NewWindowsProbe(p, config, opts) From 5222280fb9105593d8d5a29ced70fc54a72d8c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9na=C3=AFc=20Huard?= Date: Tue, 28 Nov 2023 19:40:16 +0100 Subject: [PATCH 59/87] [CONTINT-258] Fix the test cache deactivation in `inv new-e2e-tests.run` (#21093) [CONTINT-258] Fix the test cache deactivation in `inv new-e2e-tests.run` --- tasks/new_e2e_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/new_e2e_tests.py b/tasks/new_e2e_tests.py index cafc4dfb1ffe1..c136430277a8f 100644 --- a/tasks/new_e2e_tests.py +++ b/tasks/new_e2e_tests.py @@ -93,7 +93,7 @@ def run( test_run_arg = f"-run {test_run_name}" cmd = f'gotestsum --format {gotestsum_format} ' - cmd += '{junit_file_flag} --packages="{packages}" -- -ldflags="-X {REPO_PATH}/test/new-e2e/tests/containers.GitCommit={commit}" {verbose} -mod={go_mod} -vet=off -timeout {timeout} -tags {go_build_tags} {nocache} {run} {skip} {coverage_opt} {test_run_arg} -args {osversion} {platform} {major_version} {arch} {flavor} {cws_supported_osversion} {keep_stacks}' + cmd += '{junit_file_flag} --packages="{packages}" -- -ldflags="-X {REPO_PATH}/test/new-e2e/tests/containers.GitCommit={commit}" {verbose} -mod={go_mod} -vet=off -timeout {timeout} -tags "{go_build_tags}" {nocache} {run} {skip} {coverage_opt} {test_run_arg} -args {osversion} {platform} {major_version} {arch} {flavor} {cws_supported_osversion} {keep_stacks}' args = { "go_mod": "mod", From 25d28bcca1a6aa30fbd6ef1b8dedf5476bfa494a Mon Sep 17 00:00:00 2001 From: Kacper <89013263+kacper-murzyn@users.noreply.github.com> Date: Wed, 29 Nov 2023 09:59:55 +0100 Subject: [PATCH 60/87] Add agent release management team to .ddqa config (#21103) * Add agent release management team to .ddqa config * Remove whitespace --- .ddqa/config.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.ddqa/config.toml b/.ddqa/config.toml index 99a5ea254056f..63bca53c98c08 100644 --- a/.ddqa/config.toml +++ b/.ddqa/config.toml @@ -265,3 +265,14 @@ jira_statuses = [ ] github_team = "apm-onboarding" github_labels = ["team/apm-onboarding"] + +[teams."Agent Release Management"] +jira_project = "AGNTR" +jira_issue_type = "Task" +jira_statuses = [ + "To Do", + "In Progress", + "Done", +] +github_team = "agent-release-management" +github_labels = ["team/agent-release-management"] From cb69f940474c763c1d091f40b9a441b1234c6de6 Mon Sep 17 00:00:00 2001 From: Pierre Gimalac Date: Wed, 29 Nov 2023 10:46:41 +0100 Subject: [PATCH 61/87] Add a go-work invoke task (#21101) * feat: add go-work invoke task * move semver import inside task --- tasks/__init__.py | 2 ++ tasks/modules.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/tasks/__init__.py b/tasks/__init__.py index aac3f4393071e..82554f9f2711d 100644 --- a/tasks/__init__.py +++ b/tasks/__init__.py @@ -18,6 +18,7 @@ epforwarder, github_tasks, kmt, + modules, msi, new_e2e_tests, package, @@ -117,6 +118,7 @@ ns.add_task(fuzz) ns.add_task(go_fix) ns.add_task(build_messagetable) +ns.add_task(modules.go_work) ns.add_task(get_modified_packages) ns.add_task(send_unit_tests_stats) diff --git a/tasks/modules.py b/tasks/modules.py index 9f853d4d61cff..9d1a26ce7757d 100644 --- a/tasks/modules.py +++ b/tasks/modules.py @@ -4,6 +4,10 @@ import sys from contextlib import contextmanager +from invoke import Context, task + +from tasks.libs.common.color import color_message + FORBIDDEN_CODECOV_FLAG_CHARS = re.compile(r'[^\w\.\-]') @@ -235,3 +239,40 @@ def generate_dummy_package(ctx, folder): finally: # delete test_folder to avoid FileExistsError while running this task again ctx.run(f"rm -rf ./{folder}") + + +@task +def go_work(_: Context): + """ + Create a go.work file using the module list contained in DEFAULT_MODULES + and the go version contained in the file .go-version. + If there is already a go.work file, it is renamed go.work.backup and a warning is printed. + """ + from semver import Version + + print( + color_message( + "WARNING: Using a go.work file is not supported and can cause weird errors " + "when compiling the agent or running tests.\n" + "Remember to export GOWORK=off to avoid these issues.\n", + "orange", + ), + file=sys.stderr, + ) + + # read go version from the .go-version file, removing the bugfix part of the version + + with open(".go-version") as f: + go_version = Version.parse(f.read().strip()) + go_version = f"{go_version.major}.{go_version.minor}" + + if os.path.exists("go.work"): + print("go.work already exists. Renaming to go.work.backup") + os.rename("go.work", "go.work.backup") + + with open("go.work", "w") as f: + f.write(f"go {go_version}\n\nuse (\n") + for mod in DEFAULT_MODULES.values(): + prefix = "" if mod.condition() else "//" + f.write(f"\t{prefix}{mod.path}\n") + f.write(")\n") From 6fc3ef9c42c2cf78850b904b492d313f7b6d53b1 Mon Sep 17 00:00:00 2001 From: Kacper <89013263+kacper-murzyn@users.noreply.github.com> Date: Wed, 29 Nov 2023 11:19:28 +0100 Subject: [PATCH 62/87] tag-devel task added (#21145) --- tasks/release.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tasks/release.py b/tasks/release.py index a473f3db453d8..324fc28f28341 100644 --- a/tasks/release.py +++ b/tasks/release.py @@ -772,9 +772,13 @@ def __get_force_option(force: bool) -> str: return force_option -def __tag_single_module(ctx, module, agent_version, commit, push, force_option): +def __tag_single_module(ctx, module, agent_version, commit, push, force_option, devel): """Tag a given module.""" for tag in module.tag(agent_version): + + if devel: + tag += "-devel" + ok = try_git_command( ctx, f"git tag -m {tag} {tag} {commit}{force_option}", @@ -789,7 +793,7 @@ def __tag_single_module(ctx, module, agent_version, commit, push, force_option): @task -def tag_modules(ctx, agent_version, commit="HEAD", verify=True, push=True, force=False): +def tag_modules(ctx, agent_version, commit="HEAD", verify=True, push=True, force=False, devel=False): """ Create tags for Go nested modules for a given Datadog Agent version. The version should be given as an Agent 7 version. @@ -798,6 +802,7 @@ def tag_modules(ctx, agent_version, commit="HEAD", verify=True, push=True, force * --verify checks for correctness on the Agent version (on by default). * --push will push the tags to the origin remote (on by default). * --force will allow the task to overwrite existing tags. Needed to move existing tags (off by default). + * --devel will create -devel tags (used after creation of the release branch) Examples: inv -e release.tag-modules 7.27.0 # Create tags and push them to origin @@ -812,13 +817,13 @@ def tag_modules(ctx, agent_version, commit="HEAD", verify=True, push=True, force for module in DEFAULT_MODULES.values(): # Skip main module; this is tagged at tag_version via __tag_single_module. if module.should_tag and module.path != ".": - __tag_single_module(ctx, module, agent_version, commit, push, force_option) + __tag_single_module(ctx, module, agent_version, commit, push, force_option, devel) print(f"Created module tags for version {agent_version}") @task -def tag_version(ctx, agent_version, commit="HEAD", verify=True, push=True, force=False): +def tag_version(ctx, agent_version, commit="HEAD", verify=True, push=True, force=False, devel=False): """ Create tags for a given Datadog Agent version. The version should be given as an Agent 7 version. @@ -827,6 +832,7 @@ def tag_version(ctx, agent_version, commit="HEAD", verify=True, push=True, force * --verify checks for correctness on the Agent version (on by default). * --push will push the tags to the origin remote (on by default). * --force will allow the task to overwrite existing tags. Needed to move existing tags (off by default). + * --devel will create -devel tags (used after creation of the release branch) Examples: inv -e release.tag-version 7.27.0 # Create tags and push them to origin @@ -838,10 +844,16 @@ def tag_version(ctx, agent_version, commit="HEAD", verify=True, push=True, force # Always tag the main module force_option = __get_force_option(force) - __tag_single_module(ctx, DEFAULT_MODULES["."], agent_version, commit, push, force_option) + __tag_single_module(ctx, DEFAULT_MODULES["."], agent_version, commit, push, force_option, devel) print(f"Created tags for version {agent_version}") +@task +def tag_devel(ctx, agent_version, commit="HEAD", verify=True, push=True, force=False): + tag_version(ctx, agent_version, commit, verify, push, force, devel=True) + tag_modules(ctx, agent_version, commit, verify, push, force, devel=True) + + def current_version(ctx, major_version) -> Version: return _create_version_from_match(VERSION_RE.search(get_version(ctx, major_version=major_version))) From be34835d5c1419f9c82f35cdf750d30d5eab17c5 Mon Sep 17 00:00:00 2001 From: Sylvain Afchain Date: Wed, 29 Nov 2023 11:32:51 +0100 Subject: [PATCH 63/87] [CWS] remove temporarily the mount device fallback (#21153) --- pkg/security/resolvers/mount/resolver.go | 9 ++++++--- pkg/security/resolvers/mount/resolver_test.go | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pkg/security/resolvers/mount/resolver.go b/pkg/security/resolvers/mount/resolver.go index d1edb070630ce..1a8ec19da3bef 100644 --- a/pkg/security/resolvers/mount/resolver.go +++ b/pkg/security/resolvers/mount/resolver.go @@ -256,6 +256,7 @@ func (mr *Resolver) lookupByMountID(mountID uint32) *model.Mount { return mr.mounts[mountID] } +/* func (mr *Resolver) lookupByDevice(device uint32) *model.Mount { var result *model.Mount @@ -271,16 +272,18 @@ func (mr *Resolver) lookupByDevice(device uint32) *model.Mount { return result } +*/ -func (mr *Resolver) lookupMount(mountID uint32, device uint32, allowFallbacks bool) *model.Mount { +func (mr *Resolver) lookupMount(mountID uint32, _ uint32, allowFallbacks bool) *model.Mount { if m := mr.lookupByMountID(mountID); m != nil { return m } if allowFallbacks { - if m := mr.lookupByDevice(device); m != nil { + // TODO(safchain) reintroduce using a namespace aware approach + /*if m := mr.lookupByDevice(device); m != nil { return m - } + }*/ if m := mr.getFromRedemption(mountID); m != nil { return m diff --git a/pkg/security/resolvers/mount/resolver_test.go b/pkg/security/resolvers/mount/resolver_test.go index 2ff231ed3b613..bb86ba1afe94d 100644 --- a/pkg/security/resolvers/mount/resolver_test.go +++ b/pkg/security/resolvers/mount/resolver_test.go @@ -110,7 +110,7 @@ func TestMountResolver(t *testing.T) { }, }, }, - { + /*{ "insert_device", args{ []event{ @@ -137,7 +137,7 @@ func TestMountResolver(t *testing.T) { }, }, }, - }, + },*/ { "remove_overlay", args{ From 5b6ab7de934388f6702e0f96400d42018f4f8756 Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Wed, 29 Nov 2023 11:34:56 +0100 Subject: [PATCH 64/87] [CWS] fix new e2e cws (#21151) * bit of cleanup of the new CWS e2e tests * remove `WaitAgentLogs` since it's broken * use new `waitAgentLogs` based on VM execution directly * no need to enable system probe (this is for network) * improve agent rule name * remove useless `DD_SITE` * `default.policy` -> `test.policy` * add entry in CODEOWNERS * add `new-e2e-cws-main` * fix junit upload dependency and add TEAM to cws e2e jobs --- .github/CODEOWNERS | 1 + .gitlab/e2e.yml | 10 ++++++ .gitlab/e2e_test_junit_upload.yml | 1 + test/new-e2e/pkg/utils/e2e/client/agent.go | 7 ---- .../pkg/utils/e2e/client/agent_cmd_runner.go | 20 ----------- .../tests/cws/config/e2e-system-probe.yaml | 3 +- test/new-e2e/tests/cws/e2e_cws_test.go | 34 +++++++++++++------ test/new-e2e/tests/cws/lib/app.go | 22 ++++++------ test/new-e2e/tests/cws/lib/const.go | 2 +- test/new-e2e/tests/cws/lib/logs.go | 4 +-- 10 files changed, 49 insertions(+), 55 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dbd8d75bb04be..73614974f722e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -480,6 +480,7 @@ /test/new-e2e/tests/language-detection @DataDog/processes /test/new-e2e/tests/ndm @DataDog/network-device-monitoring /test/new-e2e/tests/npm @DataDog/Networks +/test/new-e2e/tests/cws @DataDog/agent-security /test/new-e2e/tests/agent-platform @DataDog/agent-platform /test/system/ @DataDog/agent-shared-components /test/system/dogstatsd/ @DataDog/agent-metrics-logs diff --git a/.gitlab/e2e.yml b/.gitlab/e2e.yml index a31da4bbf49a2..58bfb7ba0815e 100644 --- a/.gitlab/e2e.yml +++ b/.gitlab/e2e.yml @@ -259,6 +259,16 @@ new-e2e-cws-dev: needs: [] variables: TARGETS: ./tests/cws + TEAM: csm-threats-agent + # Temporary, until we manage to stabilize those tests. + allow_failure: true + +new-e2e-cws-main: + extends: .new_e2e_template + rules: !reference [.on_main_and_no_skip_e2e] + variables: + TARGETS: ./tests/cws + TEAM: csm-threats-agent # Temporary, until we manage to stabilize those tests. allow_failure: true diff --git a/.gitlab/e2e_test_junit_upload.yml b/.gitlab/e2e_test_junit_upload.yml index ee901fb168f15..73d69ad8aa887 100644 --- a/.gitlab/e2e_test_junit_upload.yml +++ b/.gitlab/e2e_test_junit_upload.yml @@ -20,6 +20,7 @@ e2e_test_junit_upload: - new-e2e-agent-platform-install-script-debian-dogstatsd-a7-x86_64 - new-e2e-agent-platform-install-script-debian-heroku-agent-a7-x86_64 - new-e2e-npm-main + - new-e2e-cws-main script: - set +x - export DATADOG_API_KEY=$(aws ssm get-parameter --region us-east-1 --name ci.datadog-agent.datadog_api_key_org2 --with-decryption --query "Parameter.Value" --out text) diff --git a/test/new-e2e/pkg/utils/e2e/client/agent.go b/test/new-e2e/pkg/utils/e2e/client/agent.go index 2edb3460c2628..ea2e871fb9e9b 100644 --- a/test/new-e2e/pkg/utils/e2e/client/agent.go +++ b/test/new-e2e/pkg/utils/e2e/client/agent.go @@ -58,11 +58,4 @@ type Agent interface { // Retries every 100 ms up to timeout. // Returns error on failure. waitForReadyTimeout(timeout time.Duration) error - - // WaitAgentLogs waits for the agent log corresponding to the pattern - // agent-name can be: datadog-agent, system-probe, security-agent - // pattern: is the log that we are looking for - // Retries every 500 ms up to timeout. - // Returns error on failure. - WaitAgentLogs(agentName string, pattern string) error } diff --git a/test/new-e2e/pkg/utils/e2e/client/agent_cmd_runner.go b/test/new-e2e/pkg/utils/e2e/client/agent_cmd_runner.go index 6366dee03cd95..216c84084a9cf 100644 --- a/test/new-e2e/pkg/utils/e2e/client/agent_cmd_runner.go +++ b/test/new-e2e/pkg/utils/e2e/client/agent_cmd_runner.go @@ -7,7 +7,6 @@ package client import ( "errors" - "fmt" "strings" "testing" "time" @@ -158,22 +157,3 @@ func (agent *agentCommandRunner) waitForReadyTimeout(timeout time.Duration) erro }, backoff.WithMaxRetries(backoff.NewConstantBackOff(interval), uint64(maxRetries))) return err } - -// WaitAgentLogs waits for the agent log corresponding to the pattern -// agent-name can be: datadog-agent, system-probe, security-agent -// pattern: is the log that we are looking for -// Retries every 500 ms up to timeout. -// Returns error on failure. -func (agent *agentCommandRunner) WaitAgentLogs(agentName string, pattern string) error { - err := backoff.Retry(func() error { - output, err := agent.executeAgentCmdWithError([]string{fmt.Sprintf("cat /var/log/datadog/%s.log", agentName)}) - if err != nil { - return err - } - if strings.Contains(output, pattern) { - return nil - } - return errors.New("no log found") - }, backoff.WithMaxRetries(backoff.NewConstantBackOff(500*time.Millisecond), 60)) - return err -} diff --git a/test/new-e2e/tests/cws/config/e2e-system-probe.yaml b/test/new-e2e/tests/cws/config/e2e-system-probe.yaml index 1345b623f037a..31e438063b8f6 100644 --- a/test/new-e2e/tests/cws/config/e2e-system-probe.yaml +++ b/test/new-e2e/tests/cws/config/e2e-system-probe.yaml @@ -1,7 +1,6 @@ system_probe_config: - enabled: true log_level: trace runtime_security_config: enabled: true log_patterns: - - module.APIServer.*" \ No newline at end of file + - module.APIServer.*" diff --git a/test/new-e2e/tests/cws/e2e_cws_test.go b/test/new-e2e/tests/cws/e2e_cws_test.go index 6a4d888ed4c63..ed29149b127cf 100644 --- a/test/new-e2e/tests/cws/e2e_cws_test.go +++ b/test/new-e2e/tests/cws/e2e_cws_test.go @@ -7,12 +7,14 @@ package cws import ( _ "embed" + "errors" "fmt" "strconv" "strings" "testing" "time" + "github.com/cenkalti/backoff" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -27,7 +29,7 @@ import ( type agentSuite struct { e2e.Suite[e2e.AgentEnv] - apiClient cws.MyAPIClient + apiClient *cws.APIClient signalRuleID string agentRuleID string dirname string @@ -48,7 +50,6 @@ var systemProbeConfig string var securityAgentConfig string func TestAgentSuite(t *testing.T) { - e2e.Run(t, &agentSuite{}, e2e.AgentStackDef( e2e.WithVMParams(ec2params.WithName("cws-e2e-tests")), e2e.WithAgentParams( @@ -60,19 +61,18 @@ func TestAgentSuite(t *testing.T) { } func (a *agentSuite) SetupSuite() { - // Create temporary directory tempDir := a.Env().VM.Execute("mktemp -d") a.dirname = strings.TrimSuffix(tempDir, "\n") a.filename = fmt.Sprintf("%s/secret", a.dirname) a.testID = uuid.NewString()[:4] a.desc = fmt.Sprintf("e2e test rule %s", a.testID) - a.agentRuleName = fmt.Sprintf("e2e_agent_rule_%s", a.testID) + a.agentRuleName = fmt.Sprintf("new_e2e_agent_rule_%s", a.testID) a.Suite.SetupSuite() + a.apiClient = cws.NewAPIClient() } func (a *agentSuite) TearDownSuite() { - if len(a.signalRuleID) != 0 { a.apiClient.DeleteSignalRule(a.signalRuleID) } @@ -84,8 +84,6 @@ func (a *agentSuite) TearDownSuite() { } func (a *agentSuite) TestOpenSignal() { - a.apiClient = cws.NewAPIClient() - // Create CWS Agent rule rule := fmt.Sprintf("open.file.path == \"%s\"", a.filename) res, err := a.apiClient.CreateCWSAgentRule(a.agentRuleName, a.desc, rule) @@ -102,11 +100,11 @@ func (a *agentSuite) TestOpenSignal() { assert.Equal(a.T(), isReady, true, "Agent should be ready") // Check if system-probe has started - err = a.Env().Agent.WaitAgentLogs("system-probe", cws.SystemProbeStartLog) + err = a.waitAgentLogs("system-probe", cws.SystemProbeStartLog) require.NoError(a.T(), err, "system-probe could not start") // Check if security-agent has started - err = a.Env().Agent.WaitAgentLogs("security-agent", cws.SecurityStartLog) + err = a.waitAgentLogs("security-agent", cws.SecurityStartLog) require.NoError(a.T(), err, "security-agent could not start") // Download policies @@ -117,7 +115,7 @@ func (a *agentSuite) TestOpenSignal() { require.NoError(a.T(), err, "Could not get APP KEY") a.EventuallyWithT(func(c *assert.CollectT) { - policies := a.Env().VM.Execute(fmt.Sprintf("DD_APP_KEY=%s DD_API_KEY=%s DD_SITE=datadoghq.com %s runtime policy download", appKey, apiKey, cws.SecurityAgentPath)) + policies := a.Env().VM.Execute(fmt.Sprintf("DD_APP_KEY=%s DD_API_KEY=%s %s runtime policy download", appKey, apiKey, cws.SecurityAgentPath)) assert.NotEmpty(c, policies, "should not be empty") a.policies = policies }, 5*time.Minute, 10*time.Second) @@ -145,7 +143,7 @@ func (a *agentSuite) TestOpenSignal() { a.Env().VM.Execute(fmt.Sprintf("touch %s", a.filename)) // Check agent event - err = a.Env().Agent.WaitAgentLogs("security-agent", "Successfully posted payload to") + err = a.waitAgentLogs("security-agent", "Successfully posted payload to") require.NoError(a.T(), err, "could not send payload") // Check app signal @@ -156,3 +154,17 @@ func (a *agentSuite) TestOpenSignal() { assert.Contains(a.T(), agentContext["rule_id"], a.agentRuleName, "unable to find tag") } + +func (a *agentSuite) waitAgentLogs(agentName string, pattern string) error { + err := backoff.Retry(func() error { + output, err := a.Env().VM.ExecuteWithError(fmt.Sprintf("cat /var/log/datadog/%s.log", agentName)) + if err != nil { + return err + } + if strings.Contains(output, pattern) { + return nil + } + return errors.New("no log found") + }, backoff.WithMaxRetries(backoff.NewConstantBackOff(500*time.Millisecond), 60)) + return err +} diff --git a/test/new-e2e/tests/cws/lib/app.go b/test/new-e2e/tests/cws/lib/app.go index 284cc17acf6b4..d4ab53996e3f1 100644 --- a/test/new-e2e/tests/cws/lib/app.go +++ b/test/new-e2e/tests/cws/lib/app.go @@ -15,14 +15,14 @@ import ( "github.com/DataDog/datadog-api-client-go/api/v2/datadog" ) -// MyAPIClient represents the datadog API context -type MyAPIClient struct { +// APIClient represents the datadog API context +type APIClient struct { api *datadog.APIClient ctx context.Context } // NewAPIClient initialise a client with the API and APP keys -func NewAPIClient() MyAPIClient { +func NewAPIClient() *APIClient { apiKey, _ := runner.GetProfile().SecretStore().Get(parameters.APIKey) appKey, _ := runner.GetProfile().SecretStore().Get(parameters.APPKey) ctx := context.WithValue( @@ -40,15 +40,14 @@ func NewAPIClient() MyAPIClient { cfg := datadog.NewConfiguration() - apiClient := MyAPIClient{ + return &APIClient{ api: datadog.NewAPIClient(cfg), ctx: ctx, } - return apiClient } // GetAppLog returns the logs corresponding to the query -func (c MyAPIClient) GetAppLog(query string) (*datadog.LogsListResponse, error) { +func (c *APIClient) GetAppLog(query string) (*datadog.LogsListResponse, error) { sort := datadog.LOGSSORT_TIMESTAMP_ASCENDING body := datadog.LogsListRequest{ @@ -75,7 +74,7 @@ func (c MyAPIClient) GetAppLog(query string) (*datadog.LogsListResponse, error) } // GetAppSignal returns the signal corresponding to the query -func (c MyAPIClient) GetAppSignal(query string) (*datadog.SecurityMonitoringSignalsListResponse, error) { +func (c *APIClient) GetAppSignal(query string) (*datadog.SecurityMonitoringSignalsListResponse, error) { now := time.Now().UTC() queryFrom := now.Add(-15 * time.Minute) sort := datadog.SECURITYMONITORINGSIGNALSSORT_TIMESTAMP_ASCENDING @@ -105,7 +104,7 @@ func (c MyAPIClient) GetAppSignal(query string) (*datadog.SecurityMonitoringSign } // CreateCwsSignalRule creates a cws signal rule -func (c MyAPIClient) CreateCwsSignalRule(name string, msg string, agentRuleID string, tags []string) (*datadog.SecurityMonitoringRuleResponse, error) { +func (c *APIClient) CreateCwsSignalRule(name string, msg string, agentRuleID string, tags []string) (*datadog.SecurityMonitoringRuleResponse, error) { if tags == nil { tags = []string{} } @@ -157,8 +156,7 @@ func (c MyAPIClient) CreateCwsSignalRule(name string, msg string, agentRuleID st } // CreateCWSAgentRule creates a cws agent rule -func (c MyAPIClient) CreateCWSAgentRule(name string, msg string, secl string) (*datadog.CloudWorkloadSecurityAgentRuleResponse, error) { - +func (c *APIClient) CreateCWSAgentRule(name string, msg string, secl string) (*datadog.CloudWorkloadSecurityAgentRuleResponse, error) { body := datadog.CloudWorkloadSecurityAgentRuleCreateRequest{ Data: datadog.CloudWorkloadSecurityAgentRuleCreateData{ Attributes: datadog.CloudWorkloadSecurityAgentRuleCreateAttributes{ @@ -180,13 +178,13 @@ func (c MyAPIClient) CreateCWSAgentRule(name string, msg string, secl string) (* } // DeleteSignalRule deletes a signal rule -func (c MyAPIClient) DeleteSignalRule(ruleID string) error { +func (c *APIClient) DeleteSignalRule(ruleID string) error { _, err := c.api.SecurityMonitoringApi.DeleteSecurityMonitoringRule(c.ctx, ruleID) return err } // DeleteAgentRule deletes an agent rule -func (c MyAPIClient) DeleteAgentRule(ruleID string) error { +func (c *APIClient) DeleteAgentRule(ruleID string) error { _, err := c.api.CloudWorkloadSecurityApi.DeleteCloudWorkloadSecurityAgentRule(c.ctx, ruleID) return err } diff --git a/test/new-e2e/tests/cws/lib/const.go b/test/new-e2e/tests/cws/lib/const.go index 8dba4b4819a66..aec74bda98d00 100644 --- a/test/new-e2e/tests/cws/lib/const.go +++ b/test/new-e2e/tests/cws/lib/const.go @@ -16,5 +16,5 @@ const ( SecurityAgentPath = "/opt/datadog-agent/embedded/bin/security-agent" // PoliciesPath is the path of the default runtime security policies - PoliciesPath = "/etc/datadog-agent/runtime-security.d/default.policy" + PoliciesPath = "/etc/datadog-agent/runtime-security.d/test.policy" ) diff --git a/test/new-e2e/tests/cws/lib/logs.go b/test/new-e2e/tests/cws/lib/logs.go index f60d5bd8fef3a..eb949f3f70ad8 100644 --- a/test/new-e2e/tests/cws/lib/logs.go +++ b/test/new-e2e/tests/cws/lib/logs.go @@ -15,7 +15,7 @@ import ( ) // WaitAppLogs waits for the app log corresponding to the query -func WaitAppLogs(apiClient MyAPIClient, query string) (*datadog.LogAttributes, error) { +func WaitAppLogs(apiClient *APIClient, query string) (*datadog.LogAttributes, error) { query = fmt.Sprintf("host:cws-new-e2e-test-host %s", query) var resp *datadog.LogAttributes err := backoff.Retry(func() error { @@ -33,7 +33,7 @@ func WaitAppLogs(apiClient MyAPIClient, query string) (*datadog.LogAttributes, e } // WaitAppSignal waits for the signal corresponding to the query -func WaitAppSignal(apiClient MyAPIClient, query string) (*datadog.SecurityMonitoringSignalAttributes, error) { +func WaitAppSignal(apiClient *APIClient, query string) (*datadog.SecurityMonitoringSignalAttributes, error) { var resp *datadog.SecurityMonitoringSignalAttributes err := backoff.Retry(func() error { tmpResp, err := apiClient.GetAppSignal(query) From 90ffa0f6f8351f74758c6e22ec6fd4a4bf461229 Mon Sep 17 00:00:00 2001 From: Sylvain Afchain Date: Wed, 29 Nov 2023 13:02:29 +0100 Subject: [PATCH 65/87] [CWS] remove useless eRPC dentry functions (#18949) * [CWS] remove useless eRPC dentry functions * [CWS] fix kernel module test * fix tests post rebase --- .../ebpf/c/include/constants/custom.h | 2 - pkg/security/ebpf/c/include/constants/enums.h | 4 +- pkg/security/ebpf/c/include/helpers/erpc.h | 4 - .../ebpf/c/include/hooks/dentry_resolver.h | 202 ------------------ pkg/security/ebpf/probes/all.go | 2 - pkg/security/ebpf/probes/dentry.go | 14 -- pkg/security/probe/erpc/erpc.go | 4 +- pkg/security/resolvers/dentry/resolver.go | 62 +----- pkg/security/tests/dentry_test.go | 200 +++++++---------- pkg/security/tests/kernel_module_test.go | 25 ++- pkg/security/tests/module_tester.go | 2 +- 11 files changed, 99 insertions(+), 422 deletions(-) diff --git a/pkg/security/ebpf/c/include/constants/custom.h b/pkg/security/ebpf/c/include/constants/custom.h index cbea2e31a7f03..6112a27c6f87d 100644 --- a/pkg/security/ebpf/c/include/constants/custom.h +++ b/pkg/security/ebpf/c/include/constants/custom.h @@ -51,8 +51,6 @@ enum DENTRY_RESOLVER_KEYS { DR_DENTRY_RESOLVER_KERN_KEY, DR_AD_FILTER_KEY, DR_ERPC_KEY, - DR_ERPC_PARENT_KEY, - DR_ERPC_SEGMENT_KEY, }; #define DR_ERPC_BUFFER_LENGTH 8*4096 diff --git a/pkg/security/ebpf/c/include/constants/enums.h b/pkg/security/ebpf/c/include/constants/enums.h index c85a4cdaf1121..3d9f059eb3a2c 100644 --- a/pkg/security/ebpf/c/include/constants/enums.h +++ b/pkg/security/ebpf/c/include/constants/enums.h @@ -162,9 +162,9 @@ enum erpc_op { UNKNOWN_OP, DISCARD_INODE_OP, DISCARD_PID_OP, - RESOLVE_SEGMENT_OP, + RESOLVE_SEGMENT_OP, // DEPRECATED RESOLVE_PATH_OP, - RESOLVE_PARENT_OP, + RESOLVE_PARENT_OP, // DEPRECATED REGISTER_SPAN_TLS_OP, // can be used outside of the CWS, do not change the value EXPIRE_INODE_DISCARDER_OP, EXPIRE_PID_DISCARDER_OP, diff --git a/pkg/security/ebpf/c/include/helpers/erpc.h b/pkg/security/ebpf/c/include/helpers/erpc.h index 4d77b2b1c56f2..a71b5c83d1059 100644 --- a/pkg/security/ebpf/c/include/helpers/erpc.h +++ b/pkg/security/ebpf/c/include/helpers/erpc.h @@ -121,12 +121,8 @@ int __attribute__((always_inline)) handle_erpc_request(ctx_t *ctx) { } switch (op) { - case RESOLVE_SEGMENT_OP: - return handle_dr_request(ctx, data, DR_ERPC_SEGMENT_KEY); // func (dr *DentryResolver) ResolveFromERPC in the userspace code side triggers handle_dr_request case RESOLVE_PATH_OP: return handle_dr_request(ctx, data, DR_ERPC_KEY); - case RESOLVE_PARENT_OP: - return handle_dr_request(ctx, data, DR_ERPC_PARENT_KEY); case USER_SESSION_CONTEXT_OP: return handle_register_user_session(data); case REGISTER_SPAN_TLS_OP: diff --git a/pkg/security/ebpf/c/include/hooks/dentry_resolver.h b/pkg/security/ebpf/c/include/hooks/dentry_resolver.h index ea52c28a978d2..0e4a7310afef5 100644 --- a/pkg/security/ebpf/c/include/hooks/dentry_resolver.h +++ b/pkg/security/ebpf/c/include/hooks/dentry_resolver.h @@ -296,208 +296,6 @@ int tail_call_target_dentry_resolver_erpc_mmap(ctx_t *ctx) { return dentry_resolver_erpc_mmap(ctx, DR_KPROBE_OR_FENTRY); } -int __attribute__((always_inline)) dentry_resolver_segment_erpc_write_user(void *ctx) { - u32 key = 0; - u32 resolution_err = 0; - - struct dr_erpc_state_t *state = bpf_map_lookup_elem(&dr_erpc_state, &key); - if (state == NULL) { - return 0; - } - - // resolve segment and write in buffer - struct path_key_t path_key = state->key; - struct path_leaf_t *map_value = bpf_map_lookup_elem(&pathnames, &path_key); - if (map_value == NULL) { - resolution_err = DR_ERPC_CACHE_MISS; - goto exit; - } - - if (map_value->len + sizeof(key) > state->buffer_size) { - // make sure we do not write outside of the provided buffer - resolution_err = DR_ERPC_BUFFER_SIZE; - goto exit; - } - - int ret = bpf_probe_write_user((void *) state->userspace_buffer, &state->key, sizeof(state->key)); - if (ret < 0) { - resolution_err = ret == -14 ? DR_ERPC_WRITE_PAGE_FAULT : DR_ERPC_UNKNOWN_ERROR; - goto exit; - } - ret = bpf_probe_write_user((void *) state->userspace_buffer + offsetof(struct path_key_t, path_id), &state->challenge, sizeof(state->challenge)); - if (ret < 0) { - resolution_err = ret == -14 ? DR_ERPC_WRITE_PAGE_FAULT : DR_ERPC_UNKNOWN_ERROR; - goto exit; - } - - ret = bpf_probe_write_user((void *) state->userspace_buffer + sizeof(state->key), map_value->name, DR_MAX_SEGMENT_LENGTH + 1); - if (ret < 0) { - resolution_err = ret == -14 ? DR_ERPC_WRITE_PAGE_FAULT : DR_ERPC_UNKNOWN_ERROR; - goto exit; - } - -exit: - monitor_resolution_err(resolution_err); - return 0; -} - -TAIL_CALL_TARGET("dentry_resolver_segment_erpc_write_user") -int tail_call_target_dentry_resolver_segment_erpc_write_user(ctx_t *ctx) { - return dentry_resolver_segment_erpc_write_user(ctx); -} - -int __attribute__((always_inline)) dentry_resolver_segment_erpc_mmap(void *ctx) { - u32 key = 0; - u32 resolution_err = 0; - char *mmapped_userspace_buffer = NULL; - - struct dr_erpc_state_t *state = bpf_map_lookup_elem(&dr_erpc_state, &key); - if (state == NULL) { - return 0; - } - - mmapped_userspace_buffer = bpf_map_lookup_elem(&dr_erpc_buffer, &key); - if (mmapped_userspace_buffer == NULL) { - resolution_err = DR_ERPC_UNKNOWN_ERROR; - goto exit; - } - - // resolve segment and write in buffer - struct path_key_t path_key = state->key; - struct path_leaf_t *map_value = bpf_map_lookup_elem(&pathnames, &path_key); - if (map_value == NULL) { - resolution_err = DR_ERPC_CACHE_MISS; - goto exit; - } - - if (map_value->len + sizeof(key) > state->buffer_size) { - // make sure we do not write outside of the provided buffer - resolution_err = DR_ERPC_BUFFER_SIZE; - goto exit; - } - - int ret = bpf_probe_read((void *) mmapped_userspace_buffer, sizeof(state->key), &state->key); - if (ret < 0) { - resolution_err = ret == -14 ? DR_ERPC_WRITE_PAGE_FAULT : DR_ERPC_UNKNOWN_ERROR; - goto exit; - } - - ret = bpf_probe_read((void *) mmapped_userspace_buffer + (offsetof(struct path_key_t, path_id) & 0x7FFF), sizeof(state->challenge), &state->challenge); - if (ret < 0) { - resolution_err = ret == -14 ? DR_ERPC_WRITE_PAGE_FAULT : DR_ERPC_UNKNOWN_ERROR; - goto exit; - } - - ret = bpf_probe_read((void *) mmapped_userspace_buffer + (sizeof(state->key) & 0x7FFF), DR_MAX_SEGMENT_LENGTH + 1, map_value->name); - if (ret < 0) { - resolution_err = ret == -14 ? DR_ERPC_WRITE_PAGE_FAULT : DR_ERPC_UNKNOWN_ERROR; - goto exit; - } - -exit: - monitor_resolution_err(resolution_err); - return 0; -} - -TAIL_CALL_TARGET("dentry_resolver_segment_erpc_mmap") -int tail_call_target_dentry_resolver_segment_erpc_mmap(ctx_t *ctx) { - return dentry_resolver_segment_erpc_mmap(ctx); -} - -int __attribute__((always_inline)) dentry_resolver_parent_erpc_write_user(void *ctx) { - u32 key = 0; - u32 resolution_err = 0; - - struct dr_erpc_state_t *state = bpf_map_lookup_elem(&dr_erpc_state, &key); - if (state == NULL) { - return 0; - } - - // resolve segment and write in buffer - struct path_key_t path_key = state->key; - struct path_leaf_t *map_value = bpf_map_lookup_elem(&pathnames, &path_key); - if (map_value == NULL) { - resolution_err = DR_ERPC_CACHE_MISS; - goto exit; - } - - if (sizeof(map_value->parent) > state->buffer_size) { - // make sure we do not write outside of the provided buffer - resolution_err = DR_ERPC_BUFFER_SIZE; - goto exit; - } - - int ret = bpf_probe_write_user((void *) state->userspace_buffer, &map_value->parent, sizeof(map_value->parent)); - if (ret < 0) { - resolution_err = ret == -14 ? DR_ERPC_WRITE_PAGE_FAULT : DR_ERPC_UNKNOWN_ERROR; - goto exit; - } - ret = bpf_probe_write_user((void *) state->userspace_buffer + offsetof(struct path_key_t, path_id), &state->challenge, sizeof(state->challenge)); - if (ret < 0) { - resolution_err = ret == -14 ? DR_ERPC_WRITE_PAGE_FAULT : DR_ERPC_UNKNOWN_ERROR; - goto exit; - } - -exit: - monitor_resolution_err(resolution_err); - return 0; -} - -TAIL_CALL_TARGET("dentry_resolver_parent_erpc_write_user") -int tail_call_target_dentry_resolver_parent_erpc_write_user(ctx_t *ctx) { - return dentry_resolver_parent_erpc_write_user(ctx); -} - -int __attribute__((always_inline)) dentry_resolver_parent_erpc_mmap(void *ctx) { - u32 key = 0; - u32 resolution_err = 0; - char *mmapped_userspace_buffer = NULL; - - struct dr_erpc_state_t *state = bpf_map_lookup_elem(&dr_erpc_state, &key); - if (state == NULL) { - return 0; - } - - mmapped_userspace_buffer = bpf_map_lookup_elem(&dr_erpc_buffer, &key); - if (mmapped_userspace_buffer == NULL) { - resolution_err = DR_ERPC_UNKNOWN_ERROR; - goto exit; - } - - // resolve segment and write in buffer - struct path_key_t path_key = state->key; - struct path_leaf_t *map_value = bpf_map_lookup_elem(&pathnames, &path_key); - if (map_value == NULL) { - resolution_err = DR_ERPC_CACHE_MISS; - goto exit; - } - - if (sizeof(map_value->parent) > state->buffer_size) { - // make sure we do not write outside of the provided buffer - resolution_err = DR_ERPC_BUFFER_SIZE; - goto exit; - } - - int ret = bpf_probe_read((void *) mmapped_userspace_buffer, sizeof(map_value->parent), &map_value->parent); - if (ret < 0) { - resolution_err = ret == -14 ? DR_ERPC_WRITE_PAGE_FAULT : DR_ERPC_UNKNOWN_ERROR; - goto exit; - } - ret = bpf_probe_read((void *) mmapped_userspace_buffer + (offsetof(struct path_key_t, path_id) & 0x7FFF), sizeof(state->challenge), &state->challenge); - if (ret < 0) { - resolution_err = ret == -14 ? DR_ERPC_WRITE_PAGE_FAULT : DR_ERPC_UNKNOWN_ERROR; - goto exit; - } - -exit: - monitor_resolution_err(resolution_err); - return 0; -} - -TAIL_CALL_TARGET("dentry_resolver_parent_erpc_mmap") -int tail_call_target_dentry_resolver_parent_erpc_mmap(ctx_t *ctx) { - return dentry_resolver_parent_erpc_mmap(ctx); -} TAIL_CALL_TARGET("dentry_resolver_ad_filter") int tail_call_target_dentry_resolver_ad_filter(ctx_t *ctx) { diff --git a/pkg/security/ebpf/probes/all.go b/pkg/security/ebpf/probes/all.go index ff84ce189ac02..f29b392f9bdee 100644 --- a/pkg/security/ebpf/probes/all.go +++ b/pkg/security/ebpf/probes/all.go @@ -257,8 +257,6 @@ func AllTailRoutes(ERPCDentryResolutionEnabled, networkEnabled, supportMmapableM func AllBPFProbeWriteUserProgramFunctions() []string { return []string{ "tail_call_target_dentry_resolver_erpc_write_user", - "tail_call_target_dentry_resolver_parent_erpc_write_user", - "tail_call_target_dentry_resolver_segment_erpc_write_user", } } diff --git a/pkg/security/ebpf/probes/dentry.go b/pkg/security/ebpf/probes/dentry.go index 56a25d2eab899..866ee4d104e82 100644 --- a/pkg/security/ebpf/probes/dentry.go +++ b/pkg/security/ebpf/probes/dentry.go @@ -200,20 +200,6 @@ func getDentryResolverTailCallRoutes(ERPCDentryResolutionEnabled, supportMmapabl EBPFFuncName: "tail_call_target_dentry_resolver_erpc" + ebpfSuffix, }, }, - { - ProgArrayName: dentryResolverProgs, - Key: DentryResolverParentERPCKey, - ProbeIdentificationPair: manager.ProbeIdentificationPair{ - EBPFFuncName: "tail_call_target_dentry_resolver_parent_erpc" + ebpfSuffix, - }, - }, - { - ProgArrayName: dentryResolverProgs, - Key: DentryResolverSegmentERPCKey, - ProbeIdentificationPair: manager.ProbeIdentificationPair{ - EBPFFuncName: "tail_call_target_dentry_resolver_segment_erpc" + ebpfSuffix, - }, - }, }...) } diff --git a/pkg/security/probe/erpc/erpc.go b/pkg/security/probe/erpc/erpc.go index 3970ac1cedc4a..ffe7a525b1a8e 100644 --- a/pkg/security/probe/erpc/erpc.go +++ b/pkg/security/probe/erpc/erpc.go @@ -26,11 +26,11 @@ const ( DiscardInodeOp = iota + 1 // DiscardPidOp discards a pid DiscardPidOp - // ResolveSegmentOp resolves the requested segment + // ResolveSegmentOp resolves the requested segment (DEPRECATED) ResolveSegmentOp // ResolvePathOp resolves the requested path ResolvePathOp - // ResolveParentOp resolves the parent of the provide path key + // ResolveParentOp resolves the parent of the provide path key (DEPRECATED) ResolveParentOp // RegisterSpanTLSOP is used for span TLS registration RegisterSpanTLSOP diff --git a/pkg/security/resolvers/dentry/resolver.go b/pkg/security/resolvers/dentry/resolver.go index 8593ba8a55dd7..2bf30caa80715 100644 --- a/pkg/security/resolvers/dentry/resolver.go +++ b/pkg/security/resolvers/dentry/resolver.go @@ -273,9 +273,6 @@ func (dr *Resolver) ResolveNameFromMap(pathKey model.PathKey) (string, error) { // ResolveName resolves an inode/mount ID pair to a file basename func (dr *Resolver) ResolveName(pathKey model.PathKey) string { name, err := dr.ResolveNameFromCache(pathKey) - if err != nil && dr.config.ERPCDentryResolutionEnabled { - name, err = dr.ResolveNameFromERPC(pathKey) - } if err != nil && dr.config.MapDentryResolutionEnabled { name, err = dr.ResolveNameFromMap(pathKey) } @@ -465,34 +462,6 @@ func (dr *Resolver) requestResolve(op uint8, pathKey model.PathKey) (uint32, err return challenge, dr.erpc.Request(dr.erpcRequest) } -// ResolveNameFromERPC resolves the name of the provided inode / mount id / path id -func (dr *Resolver) ResolveNameFromERPC(pathKey model.PathKey) (string, error) { - entry := counterEntry{ - resolutionType: metrics.ERPCTag, - resolution: metrics.SegmentResolutionTag, - } - - challenge, err := dr.requestResolve(erpc.ResolveSegmentOp, pathKey) - if err != nil { - dr.missCounters[entry].Inc() - return "", fmt.Errorf("unable to get the name of mountID `%d` and inode `%d` with eRPC: %w", pathKey.MountID, pathKey.Inode, err) - } - - if challenge != model.ByteOrder.Uint32(dr.erpcSegment[12:16]) { - dr.missCounters[entry].Inc() - return "", errERPCRequestNotProcessed - } - - seg := model.NullTerminatedString(dr.erpcSegment[16:]) - if len(seg) == 0 || len(seg) > 0 && seg[0] == 0 { - dr.missCounters[entry].Inc() - return "", fmt.Errorf("couldn't resolve segment (len: %d)", len(seg)) - } - - dr.hitsCounters[entry].Inc() - return seg, nil -} - func (dr *Resolver) cacheEntries(keys []model.PathKey, entries []PathEntry) error { var cacheEntry PathEntry @@ -631,32 +600,6 @@ func (dr *Resolver) ResolveParentFromCache(pathKey model.PathKey) (model.PathKey return path.Parent, nil } -// ResolveParentFromERPC resolves the parent -func (dr *Resolver) ResolveParentFromERPC(pathKey model.PathKey) (model.PathKey, error) { - entry := counterEntry{ - resolutionType: metrics.ERPCTag, - resolution: metrics.ParentResolutionTag, - } - - // create eRPC request - challenge, err := dr.requestResolve(erpc.ResolveParentOp, pathKey) - if err != nil { - dr.missCounters[entry].Inc() - return model.PathKey{}, fmt.Errorf("unable to resolve the parent of mountID `%d` and inode `%d` with eRPC: %w", pathKey.MountID, pathKey.Inode, err) - } - - if challenge != model.ByteOrder.Uint32(dr.erpcSegment[12:16]) { - dr.missCounters[entry].Inc() - return model.PathKey{}, errERPCRequestNotProcessed - } - - pathKey.Inode = model.ByteOrder.Uint64(dr.erpcSegment[0:8]) - pathKey.MountID = model.ByteOrder.Uint32(dr.erpcSegment[8:12]) - - dr.hitsCounters[entry].Inc() - return pathKey, nil -} - // ResolveParentFromMap resolves the parent func (dr *Resolver) ResolveParentFromMap(pathKey model.PathKey) (model.PathKey, error) { entry := counterEntry{ @@ -677,10 +620,7 @@ func (dr *Resolver) ResolveParentFromMap(pathKey model.PathKey) (model.PathKey, // GetParent returns the parent mount_id/inode func (dr *Resolver) GetParent(pathKey model.PathKey) (model.PathKey, error) { pathKey, err := dr.ResolveParentFromCache(pathKey) - if err != nil && dr.config.ERPCDentryResolutionEnabled { - pathKey, err = dr.ResolveParentFromERPC(pathKey) - } - if err != nil && err != errTruncatedParentsERPC && dr.config.MapDentryResolutionEnabled { + if err != nil && dr.config.MapDentryResolutionEnabled { pathKey, err = dr.ResolveParentFromMap(pathKey) } diff --git a/pkg/security/tests/dentry_test.go b/pkg/security/tests/dentry_test.go index 03ab6db26cda4..a354f4194bbbd 100644 --- a/pkg/security/tests/dentry_test.go +++ b/pkg/security/tests/dentry_test.go @@ -16,8 +16,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + "golang.org/x/sys/unix" - "github.com/DataDog/datadog-agent/pkg/security/ebpf/kernel" "github.com/DataDog/datadog-agent/pkg/security/metrics" sprobe "github.com/DataDog/datadog-agent/pkg/security/probe" "github.com/DataDog/datadog-agent/pkg/security/resolvers/dentry" @@ -25,62 +25,15 @@ import ( "github.com/DataDog/datadog-agent/pkg/security/secl/rules" ) -func validateResolution(t testing.TB, probe *sprobe.EBPFProbe, event *model.Event, testFile string, pathFnc func(model.PathKey, bool) (string, error), parentFnc func(model.PathKey) (model.PathKey, error), nameFnc func(model.PathKey) (string, error)) { - basename := path.Base(testFile) - - // Force an eRPC resolution to refresh the entry with the last generation as lost events may have invalidated the entry - res, err := pathFnc(event.Open.File.PathKey, true) - assert.Nil(t, err) - assert.Equal(t, basename, path.Base(res)) - - // there is a potential race here as a lost event can occur between the two resolutions - - // check that the path is now available from the cache - res, err = probe.Resolvers.DentryResolver.ResolveFromCache(event.Open.File.PathKey) - assert.Nil(t, err) - assert.Equal(t, basename, path.Base(res)) - - kv, err := kernel.NewKernelVersion() - assert.Nil(t, err) - - // Parent - expectedInode := getInode(t, path.Dir(testFile)) - - // the previous path resolution should habe filled the cache - pathKey, err := probe.Resolvers.DentryResolver.ResolveParentFromCache(event.Open.File.PathKey) - assert.Nil(t, err) - assert.NotZero(t, pathKey.Inode) - - // on kernel < 5.0 the cache is populated with internal inode of overlayfs. The stat syscall returns the proper inode, that is why the inodes don't match. - if event.Open.File.Filesystem != model.OverlayFS || kv.Code > kernel.Kernel5_0 { - assert.Equal(t, expectedInode, pathKey.Inode) - } - - parentKey, err := parentFnc(event.Open.File.PathKey) - assert.Nil(t, err) - assert.NotZero(t, parentKey.Inode) - assert.Equal(t, pathKey.Inode, parentKey.Inode) - - // Basename - // the previous path resolution should have filled the cache - expectedName, err := probe.Resolvers.DentryResolver.ResolveNameFromCache(event.Open.File.PathKey) - assert.Nil(t, err) - assert.Equal(t, expectedName, basename) - - expectedName, err = nameFnc(event.Open.File.PathKey) - assert.Nil(t, err) - assert.Equal(t, expectedName, basename) -} - -func TestDentryResolutionERPC(t *testing.T) { +func TestDentryPathERPC(t *testing.T) { // generate a basename up to the current limit of the agent var basename string for i := 0; i < model.MaxSegmentLength; i++ { basename += "a" } rule := &rules.RuleDefinition{ - ID: "test_erpc_rule", - Expression: fmt.Sprintf(`open.file.path == "{{.Root}}/parent/%s" && open.flags & O_CREAT != 0`, basename), + ID: "test_erpc_path_rule", + Expression: fmt.Sprintf(`open.flags & (O_CREAT|O_NOCTTY|O_NOFOLLOW) != 0 && process.file.name == "testsuite"`), } test, err := newTestModule(t, nil, []*rules.RuleDefinition{rule}, withStaticOpts(testOpts{disableMapDentryResolution: true})) @@ -106,11 +59,27 @@ func TestDentryResolutionERPC(t *testing.T) { defer os.RemoveAll(dir) test.WaitSignal(t, func() error { - _, err = os.Create(testFile) - return err + file, err := os.OpenFile(testFile, os.O_CREATE|unix.O_NOCTTY|unix.O_NOFOLLOW, 0666) + if err != nil { + return err + } + file.Close() + return nil }, func(event *model.Event, rule *rules.Rule) { - assertTriggeredRule(t, rule, "test_erpc_rule") + assertTriggeredRule(t, rule, "test_erpc_path_rule") + + basename := path.Base(testFile) + res, err := p.Resolvers.DentryResolver.Resolve(event.Open.File.PathKey, true) + assert.Nil(test.t, err) + assert.Equal(test.t, basename, path.Base(res)) + + // check that the path is now available from the cache + res, err = p.Resolvers.DentryResolver.ResolveFromCache(event.Open.File.PathKey) + assert.Nil(test.t, err) + assert.Equal(test.t, basename, path.Base(res)) + + // check stats test.eventMonitor.SendStats() key := metrics.MetricDentryResolverHits + ":" + metrics.ERPCTag @@ -118,24 +87,18 @@ func TestDentryResolutionERPC(t *testing.T) { key = metrics.MetricDentryResolverHits + ":" + metrics.KernelMapsTag assert.Empty(t, test.statsdClient.Get(key)) - - validateResolution(test.t, p, event, testFile, - p.Resolvers.DentryResolver.ResolveFromERPC, - p.Resolvers.DentryResolver.ResolveParentFromERPC, - p.Resolvers.DentryResolver.ResolveNameFromERPC, - ) }) } -func TestDentryResolutionMap(t *testing.T) { +func TestDentryPathMap(t *testing.T) { // generate a basename up to the current limit of the agent var basename string for i := 0; i < model.MaxSegmentLength; i++ { basename += "a" } rule := &rules.RuleDefinition{ - ID: "test_map_rule", - Expression: fmt.Sprintf(`open.file.path == "{{.Root}}/parent/%s" && open.flags & O_CREAT != 0`, basename), + ID: "test_map_path_rule", + Expression: fmt.Sprintf(`open.flags & (O_CREAT|O_NOCTTY|O_NOFOLLOW) != 0 && process.file.name == "testsuite"`), } test, err := newTestModule(t, nil, []*rules.RuleDefinition{rule}, withStaticOpts(testOpts{disableERPCDentryResolution: true})) @@ -153,106 +116,99 @@ func TestDentryResolutionMap(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Remove(testFile) dir := path.Dir(testFile) if err := os.MkdirAll(dir, 0777); err != nil { t.Fatalf("failed to create directory: %s", err) } - defer os.Remove(dir) + defer os.RemoveAll(dir) test.WaitSignal(t, func() error { - _, err := os.Create(testFile) + file, err := os.OpenFile(testFile, os.O_CREATE|unix.O_NOCTTY|unix.O_NOFOLLOW, 0666) if err != nil { return err } + file.Close() return nil }, func(event *model.Event, rule *rules.Rule) { - assertTriggeredRule(t, rule, "test_map_rule") + assertTriggeredRule(t, rule, "test_map_path_rule") - test.eventMonitor.SendStats() + basename := path.Base(testFile) - key := metrics.MetricDentryResolverHits + ":" + metrics.KernelMapsTag - assert.NotEmpty(t, test.statsdClient.Get(key)) + res, err := p.Resolvers.DentryResolver.Resolve(event.Open.File.PathKey, true) + assert.Nil(test.t, err) + assert.Equal(test.t, basename, path.Base(res)) + + // check that the path is now available from the cache + res, err = p.Resolvers.DentryResolver.ResolveFromCache(event.Open.File.PathKey) + assert.Nil(test.t, err) + assert.Equal(test.t, basename, path.Base(res)) - key = metrics.MetricDentryResolverHits + ":" + metrics.ERPCTag + // check stats + test.eventMonitor.SendStats() + + key := metrics.MetricDentryResolverHits + ":" + metrics.ERPCTag assert.Empty(t, test.statsdClient.Get(key)) - validateResolution(test.t, p, event, testFile, - p.Resolvers.DentryResolver.ResolveFromMap, - p.Resolvers.DentryResolver.ResolveParentFromMap, - p.Resolvers.DentryResolver.ResolveNameFromMap, - ) + key = metrics.MetricDentryResolverHits + ":" + metrics.KernelMapsTag + assert.NotEmpty(t, test.statsdClient.Get(key)) }) } -func BenchmarkERPCDentryResolutionSegment(b *testing.B) { +func TestDentryName(t *testing.T) { + // generate a basename up to the current limit of the agent + var basename string + for i := 0; i < model.MaxSegmentLength; i++ { + basename += "a" + } rule := &rules.RuleDefinition{ - ID: "test_rule", - Expression: `open.file.path == "{{.Root}}/aa/bb/cc/dd/ee" && open.flags & O_CREAT != 0`, + ID: "test_dentry_name_rule", + Expression: fmt.Sprintf(`open.flags & (O_CREAT|O_NOCTTY|O_NOFOLLOW) != 0 && process.file.name == "testsuite"`), } - test, err := newTestModule(b, nil, []*rules.RuleDefinition{rule}, withStaticOpts(testOpts{disableMapDentryResolution: true})) + test, err := newTestModule(t, nil, []*rules.RuleDefinition{rule}) if err != nil { - b.Fatal(err) + t.Fatal(err) } defer test.Close() p, ok := test.probe.PlatformProbe.(*sprobe.EBPFProbe) if !ok { - b.Skip("not supported") + t.Skip("not supported") } - testFile, _, err := test.Path("aa/bb/cc/dd/ee") + testFile, _, err := test.Path("parent/" + basename) if err != nil { - b.Fatal(err) + t.Fatal(err) } - _ = os.MkdirAll(path.Dir(testFile), 0755) - - defer os.Remove(testFile) - var pathKey model.PathKey + dir := path.Dir(testFile) + if err := os.MkdirAll(dir, 0777); err != nil { + t.Fatalf("failed to create directory: %s", err) + } + defer os.RemoveAll(dir) - err = test.GetSignal(b, func() error { - fd, err := syscall.Open(testFile, syscall.O_CREAT, 0755) + test.WaitSignal(t, func() error { + file, err := os.OpenFile(testFile, os.O_CREATE|unix.O_NOCTTY|unix.O_NOFOLLOW, 0666) if err != nil { return err } - return syscall.Close(fd) - }, func(event *model.Event, _ *rules.Rule) { - pathKey = event.Open.File.PathKey - }) - if err != nil { - b.Fatal(err) - } - - // create a new dentry resolver to avoid concurrent map access errors - resolver, err := dentry.NewResolver(test.probe.Config.Probe, test.probe.StatsdClient, p.Erpc) - if err != nil { - b.Fatal(err) - } + file.Close() + return nil + }, func(event *model.Event, rule *rules.Rule) { + assertTriggeredRule(t, rule, "test_dentry_name_rule") - if err := resolver.Start(p.Manager); err != nil { - b.Fatal(err) - } - name, err := resolver.ResolveNameFromERPC(pathKey) - if err != nil { - b.Fatal(err) - } - b.Log(name) + basename := path.Base(testFile) - b.ResetTimer() - for n := 0; n < b.N; n++ { - name, err = resolver.ResolveNameFromERPC(pathKey) - if err != nil { - b.Fatal(err) - } - if len(name) == 0 || len(name) > 0 && name[0] == 0 { - b.Log("couldn't resolve segment") - } - } + // check that the path is now available from the cache + res := p.Resolvers.DentryResolver.ResolveName(event.Open.File.PathKey) + assert.Equal(test.t, basename, res) - test.Close() + // check that the path is now available from the cache + res, err = p.Resolvers.DentryResolver.ResolveNameFromCache(event.Open.File.PathKey) + assert.Nil(test.t, err) + assert.Equal(test.t, basename, path.Base(res)) + }) } func BenchmarkERPCDentryResolutionPath(b *testing.B) { diff --git a/pkg/security/tests/kernel_module_test.go b/pkg/security/tests/kernel_module_test.go index 377094b21aa02..a27ca2c37090c 100644 --- a/pkg/security/tests/kernel_module_test.go +++ b/pkg/security/tests/kernel_module_test.go @@ -188,15 +188,15 @@ func TestLoadModule(t *testing.T) { }, { ID: "test_load_module_with_params", - Expression: fmt.Sprintf(`load_module.name == "%s" && load_module.argv in ["toto=1"]`, testModuleName), + Expression: fmt.Sprintf(`load_module.name == "%s" && load_module.argv in ["cifs_max_pending=2"]`, testModuleName), }, { ID: "test_load_module_with_truncated_params", - Expression: fmt.Sprintf(`load_module.name == "%s" && load_module.argv in ["Lorem"]`, testModuleName), + Expression: fmt.Sprintf(`load_module.name == "%s" && load_module.argv in [r"CIFSMaxBufSize=.*"]`, testModuleName), }, { ID: "test_load_module_with_params_from_memory", - Expression: fmt.Sprintf(`load_module.name == "%s" && load_module.argv in ["toto=5"] && load_module.loaded_from_memory == true`, testModuleName), + Expression: fmt.Sprintf(`load_module.name == "%s" && load_module.argv in ["cifs_min_rcv=6"] && load_module.loaded_from_memory == true`, testModuleName), }, } @@ -261,13 +261,13 @@ func TestLoadModule(t *testing.T) { } defer f.Close() - if err = unix.FinitModule(int(f.Fd()), "toto=1 toto=2 toto=3", 0); err != nil { + if err = unix.FinitModule(int(f.Fd()), "cifs_max_pending=2 cifs_min_small=2", 0); err != nil { return fmt.Errorf("couldn't insert module: %w", err) } return unix.DeleteModule(testModuleName, 0) }, func(event *model.Event, r *rules.Rule) { assert.Equal(t, "test_load_module_with_params", r.ID, "wrong rule triggered") - assertFieldEqual(t, event, "load_module.args", "toto=1 toto=2 toto=3") + assertFieldEqual(t, event, "load_module.args", "cifs_max_pending=2 cifs_min_small=2") assertFieldEqual(t, event, "load_module.loaded_from_memory", false) assertFieldEqual(t, event, "load_module.args_truncated", false) test.validateLoadModuleSchema(t, event) @@ -281,14 +281,14 @@ func TestLoadModule(t *testing.T) { return fmt.Errorf("couldn't load module content: %w", err) } - if err = unix.InitModule(module, "toto=5"); err != nil { + if err = unix.InitModule(module, "cifs_min_rcv=6"); err != nil { return fmt.Errorf("couldn't insert module: %w", err) } return unix.DeleteModule(testModuleName, 0) }, func(event *model.Event, r *rules.Rule) { assert.Equal(t, "test_load_module_with_params_from_memory", r.ID, "wrong rule triggered") - assertFieldEqual(t, event, "load_module.argv", []string{"toto=5"}) + assertFieldEqual(t, event, "load_module.argv", []string{"cifs_min_rcv=6"}) assertFieldEqual(t, event, "load_module.loaded_from_memory", true) assertFieldEqual(t, event, "load_module.args_truncated", false) test.validateLoadModuleSchema(t, event) @@ -296,21 +296,26 @@ func TestLoadModule(t *testing.T) { }) t.Run("load_module_with_truncated_params", func(t *testing.T) { + var args []string + for i := 0; i != 10; i++ { + args = append(args, fmt.Sprintf("CIFSMaxBufSize=%d", 8192+i)) + } + test.WaitSignal(t, func() error { module, err := os.ReadFile(modulePath) if err != nil { return fmt.Errorf("couldn't load module content: %w", err) } - if err = unix.InitModule(module, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis in luctus quam. Nam purus risus, varius non massa bibendum, sollicitudin"); err != nil { + if err = unix.InitModule(module, strings.Join(args, " ")); err != nil { return fmt.Errorf("couldn't insert module: %w", err) } return unix.DeleteModule(testModuleName, 0) }, func(event *model.Event, r *rules.Rule) { assert.Equal(t, "test_load_module_with_truncated_params", r.ID, "wrong rule triggered") - assertFieldEqual(t, event, "load_module.argv", []string{"Lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit.", "Duis", "in", "luctus", "quam.", "Nam", "purus", "risus,", "varius", "non", "massa", "bibendum,"}) - assertFieldEqual(t, event, "load_module.args", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis in luctus quam. Nam purus risus, varius non massa bibendum,") + assertFieldEqual(t, event, "load_module.argv", args[0:6]) + assertFieldEqual(t, event, "load_module.args", strings.Join(args[0:6], " ")) assertFieldEqual(t, event, "load_module.loaded_from_memory", true) assertFieldEqual(t, event, "load_module.args_truncated", true) test.validateLoadModuleSchema(t, event) diff --git a/pkg/security/tests/module_tester.go b/pkg/security/tests/module_tester.go index 8fe3ab23f91cd..4da4c8f83a17a 100644 --- a/pkg/security/tests/module_tester.go +++ b/pkg/security/tests/module_tester.go @@ -676,8 +676,8 @@ func validateProcessContext(tb testing.TB, event *model.Event, probe *sprobe.Pro //nolint:deadcode,unused func validateEvent(tb testing.TB, validate func(event *model.Event, rule *rules.Rule), probe *sprobe.Probe) func(event *model.Event, rule *rules.Rule) { return func(event *model.Event, rule *rules.Rule) { - validateProcessContext(tb, event, probe) validate(event, rule) + validateProcessContext(tb, event, probe) } } From 03728b0a481ea9abea2df514b71ab5a4fc47ea93 Mon Sep 17 00:00:00 2001 From: Kevin Fairise <132568982+KevinFairise2@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:11:55 +0100 Subject: [PATCH 66/87] APL-2317 Add support for agent 6 tests and refacto a bit new-e2e install script tests (#21044) APL-2317 Add support for agent 6 tests and refacto a bit new-e2e install script tests --- .gitlab/e2e_test_junit_upload.yml | 2 +- .gitlab/new-e2e_common/testing.yml | 6 ++ .gitlab/new-e2e_testing/debian.yml | 97 ++++++++++++++----- .../install-script/install_script_test.go | 12 ++- .../tests/agent-platform/install/install.go | 2 +- 5 files changed, 87 insertions(+), 32 deletions(-) diff --git a/.gitlab/e2e_test_junit_upload.yml b/.gitlab/e2e_test_junit_upload.yml index 73d69ad8aa887..52a01639669f9 100644 --- a/.gitlab/e2e_test_junit_upload.yml +++ b/.gitlab/e2e_test_junit_upload.yml @@ -14,7 +14,7 @@ e2e_test_junit_upload: - new-e2e-agent-shared-components-main - new-e2e-agent-subcommands-main - new-e2e-language-detection-main - - new-e2e-agent-platform-install-script-debian-a7-x64 + - new-e2e-agent-platform-install-script-debian-a7-x86_64 - new-e2e-agent-platform-install-script-debian-a7-arm64 - new-e2e-agent-platform-install-script-debian-iot-agent-a7-x86_64 - new-e2e-agent-platform-install-script-debian-dogstatsd-a7-x86_64 diff --git a/.gitlab/new-e2e_common/testing.yml b/.gitlab/new-e2e_common/testing.yml index efacd2d46d2e0..e97165a712940 100644 --- a/.gitlab/new-e2e_common/testing.yml +++ b/.gitlab/new-e2e_common/testing.yml @@ -1,3 +1,9 @@ +.new-e2e_agent_a6: + rules: + !reference [.on_kitchen_tests_a6] #TODO: Change when migration is complete to another name without 'kitchen' + variables: + AGENT_MAJOR_VERSION: 6 + .new-e2e_agent_a7: rules: !reference [.on_kitchen_tests_a7] #TODO: Change when migration is complete to another name without 'kitchen' diff --git a/.gitlab/new-e2e_testing/debian.yml b/.gitlab/new-e2e_testing/debian.yml index 5c4094de0e638..00d67e5ec6111 100644 --- a/.gitlab/new-e2e_testing/debian.yml +++ b/.gitlab/new-e2e_testing/debian.yml @@ -3,8 +3,31 @@ variables: E2E_PLATFORM: debian -.new-e2e_debian_a7_x64: +.new-e2e_install_script: variables: + TARGETS: ./tests/agent-platform/install-script + TEAM: agent-platform + EXTRA_PARAMS: --osversion $E2E_OSVERS --platform $E2E_PLATFORM --cws-supported-osversion $E2E_CWS_SUPPORTED_OSVERS --major-version $AGENT_MAJOR_VERSION --arch $E2E_ARCH --flavor $FLAVOR + +.new-e2e_debian_a6_x86_64: + variables: + E2E_ARCH: x86_64 + E2E_OSVERS: "debian-9,debian-10,debian-11,debian-12" + E2E_CWS_SUPPORTED_OSVERS: "debian-10,debian-11" + E2E_BRANCH_OSVERS: "debian-11" + needs: ["deploy_deb_testing-a6_x64"] + +.new-e2e_debian_a6_arm64: + variables: + E2E_ARCH: arm64 + E2E_OSVERS: "debian-10" + E2E_CWS_SUPPORTED_OSVERS: "debian-10" + E2E_BRANCH_OSVERS: "debian-10" + needs: ["deploy_deb_testing-a6_arm64"] + +.new-e2e_debian_a7_x86_64: + variables: + E2E_ARCH: x86_64 E2E_OSVERS: "debian-9,debian-10,debian-11,debian-12" E2E_CWS_SUPPORTED_OSVERS: "debian-10,debian-11" E2E_BRANCH_OSVERS: "debian-11" @@ -12,78 +35,100 @@ .new-e2e_debian_a7_arm64: variables: + E2E_ARCH: arm64 E2E_OSVERS: "debian-10" E2E_CWS_SUPPORTED_OSVERS: "debian-10" E2E_BRANCH_OSVERS: "debian-10" needs: ["deploy_deb_testing-a7_arm64"] -new-e2e-agent-platform-install-script-debian-a7-x64: +new-e2e-agent-platform-install-script-debian-a6-x86_64: + stage: kitchen_testing + extends: + - .new_e2e_template + - .new-e2e_install_script + - .new-e2e_os_debian + - .new-e2e_debian_a6_x86_64 + - .new-e2e_agent_a6 + variables: + FLAVOR: datadog-agent + +new-e2e-agent-platform-install-script-debian-a6-arm64: stage: kitchen_testing extends: - .new_e2e_template + - .new-e2e_install_script - .new-e2e_os_debian - - .new-e2e_debian_a7_x64 + - .new-e2e_debian_a6_arm64 + - .new-e2e_agent_a6 + variables: + FLAVOR: datadog-agent + +new-e2e-agent-platform-install-script-debian-a7-x86_64: + stage: kitchen_testing + extends: + - .new_e2e_template + - .new-e2e_install_script + - .new-e2e_os_debian + - .new-e2e_debian_a7_x86_64 - .new-e2e_agent_a7 rules: !reference [.on_default_new-e2e_tests_a7] variables: - TARGETS: ./tests/agent-platform/install-script - TEAM: agent-platform - EXTRA_PARAMS: --osversion $E2E_OSVERS --platform $E2E_PLATFORM --cws-supported-osversion $E2E_CWS_SUPPORTED_OSVERS --arch x86_64 + FLAVOR: datadog-agent new-e2e-agent-platform-install-script-debian-a7-arm64: stage: kitchen_testing extends: - .new_e2e_template + - .new-e2e_install_script - .new-e2e_os_debian - .new-e2e_debian_a7_arm64 - .new-e2e_agent_a7 rules: !reference [.on_all_new-e2e_tests_a7] variables: - TARGETS: ./tests/agent-platform/install-script - TEAM: agent-platform - EXTRA_PARAMS: --osversion $E2E_OSVERS --platform $E2E_PLATFORM --cws-supported-osversion $E2E_CWS_SUPPORTED_OSVERS --arch arm64 + FLAVOR: datadog-agent new-e2e-agent-platform-install-script-debian-iot-agent-a7-x86_64: stage: kitchen_testing extends: - .new_e2e_template + - .new-e2e_install_script - .new-e2e_os_debian - - .new-e2e_debian_a7_x64 + - .new-e2e_debian_a7_x86_64 - .new-e2e_agent_a7 rules: !reference [.on_default_new-e2e_tests_a7] variables: - TARGETS: ./tests/agent-platform/install-script - TEAM: agent-platform - EXTRA_PARAMS: --osversion $E2E_OSVERS --platform $E2E_PLATFORM --cws-supported-osversion $E2E_CWS_SUPPORTED_OSVERS --arch x86_64 --flavor datadog-iot-agent + FLAVOR: datadog-iot-agent new-e2e-agent-platform-install-script-debian-dogstatsd-a7-x86_64: stage: kitchen_testing extends: - .new_e2e_template + - .new-e2e_install_script - .new-e2e_os_debian - - .new-e2e_debian_a7_x64 + - .new-e2e_debian_a7_x86_64 - .new-e2e_agent_a7 variables: - TARGETS: ./tests/agent-platform/install-script - TEAM: agent-platform - EXTRA_PARAMS: --osversion $E2E_OSVERS --platform $E2E_PLATFORM --cws-supported-osversion $E2E_CWS_SUPPORTED_OSVERS --arch x86_64 --flavor datadog-dogstatsd + FLAVOR: datadog-dogstatsd + +new-e2e-agent-platform-install-script-debian-heroku-agent-a6-x86_64: + stage: kitchen_testing + extends: + - .new_e2e_template + - .new-e2e_os_debian + - .new-e2e_debian_a6_x86_64 + - .new-e2e_agent_a6 + variables: + FLAVOR: datadog-heroku-agent new-e2e-agent-platform-install-script-debian-heroku-agent-a7-x86_64: stage: kitchen_testing extends: - .new_e2e_template - .new-e2e_os_debian - - .new-e2e_debian_a7_x64 + - .new-e2e_debian_a7_x86_64 - .new-e2e_agent_a7 variables: - TARGETS: ./tests/agent-platform/install-script - TEAM: agent-platform - EXTRA_PARAMS: --osversion $E2E_OSVERS --platform $E2E_PLATFORM --cws-supported-osversion $E2E_CWS_SUPPORTED_OSVERS --arch x86_64 --flavor datadog-heroku-agent - - - - - + FLAVOR: datadog-heroku-agent diff --git a/test/new-e2e/tests/agent-platform/install-script/install_script_test.go b/test/new-e2e/tests/agent-platform/install-script/install_script_test.go index 0349f689617d5..0dd15c909d838 100644 --- a/test/new-e2e/tests/agent-platform/install-script/install_script_test.go +++ b/test/new-e2e/tests/agent-platform/install-script/install_script_test.go @@ -32,8 +32,9 @@ import ( var osVersion = flag.String("osversion", "", "os version to test") var platform = flag.String("platform", "", "platform to test") var cwsSupportedOsVersion = flag.String("cws-supported-osversion", "", "list of os where CWS is supported") -var architecture = flag.String("arch", "x84_64", "architecture to test (x86_64, arm64))") +var architecture = flag.String("arch", "x86_64", "architecture to test (x86_64, arm64))") var flavor = flag.String("flavor", "datadog-agent", "flavor to test (datadog-agent, datadog-iot-agent, datadog-dogstatsd, datadog-fips-proxy, datadog-heroku-agent)") +var majorVersion = flag.String("major-version", "7", "major version to test (6, 7)") type installScriptSuite struct { e2e.Suite[e2e.VMEnv] @@ -75,10 +76,10 @@ func TestInstallScript(t *testing.T) { } } - t.Run(fmt.Sprintf("test install script on %s %s %s", osVers, *architecture, *flavor), func(tt *testing.T) { + t.Run(fmt.Sprintf("test install script on %s %s %s agent %s", osVers, *architecture, *flavor, *majorVersion), func(tt *testing.T) { tt.Parallel() fmt.Printf("Testing %s", osVers) - e2e.Run(tt, &installScriptSuite{cwsSupported: cwsSupported}, e2e.EC2VMStackDef(ec2params.WithImageName(platformJSON[*platform][*architecture][osVers], archMapping[*architecture], osMapping[*platform])), params.WithStackName(fmt.Sprintf("install-script-test-%v-%v-%s-%s", os.Getenv("CI_PIPELINE_ID"), osVers, *architecture, *flavor))) + e2e.Run(tt, &installScriptSuite{cwsSupported: cwsSupported}, e2e.EC2VMStackDef(ec2params.WithImageName(platformJSON[*platform][*architecture][osVers], archMapping[*architecture], osMapping[*platform])), params.WithStackName(fmt.Sprintf("install-script-test-%v-%v-%s-%s-%v", os.Getenv("CI_PIPELINE_ID"), osVers, *architecture, *flavor, *majorVersion))) }) } } @@ -106,13 +107,16 @@ func (is *installScriptSuite) AgentTest(flavor string) { unixHelper := helpers.NewUnixHelper() client := common.NewTestClient(is.Env().VM, agentClient, fileManager, unixHelper) - install.Unix(is.T(), client, installparams.WithArch(*architecture), installparams.WithFlavor(flavor)) + install.Unix(is.T(), client, installparams.WithArch(*architecture), installparams.WithFlavor(flavor), installparams.WithMajorVersion(*majorVersion)) common.CheckInstallation(is.T(), client) common.CheckAgentBehaviour(is.T(), client) common.CheckAgentStops(is.T(), client) common.CheckAgentRestarts(is.T(), client) common.CheckIntegrationInstall(is.T(), client) + if *majorVersion == "6" { + common.CheckAgentPython(is.T(), client, "2") + } common.CheckAgentPython(is.T(), client, "3") common.CheckApmEnabled(is.T(), client) common.CheckApmDisabled(is.T(), client) diff --git a/test/new-e2e/tests/agent-platform/install/install.go b/test/new-e2e/tests/agent-platform/install/install.go index 3d1abfdaf5a43..e6d73cf7abe76 100644 --- a/test/new-e2e/tests/agent-platform/install/install.go +++ b/test/new-e2e/tests/agent-platform/install/install.go @@ -42,7 +42,7 @@ func Unix(t *testing.T, client *common.TestClient, options ...installparams.Opti } t.Run("Installing the agent", func(tt *testing.T) { - cmd := fmt.Sprintf(`DD_API_KEY="aaaaaaaaaa" %v DD_SITE="datadoghq.eu" bash -c "$(curl -L https://s3.amazonaws.com/dd-agent/scripts/install_script_agent7.sh)"`, commandLine) + cmd := fmt.Sprintf(`DD_API_KEY="aaaaaaaaaa" %v DD_SITE="datadoghq.eu" bash -c "$(curl -L https://s3.amazonaws.com/dd-agent/scripts/install_script_agent%v.sh)"`, commandLine, params.MajorVersion) output, err := client.VMClient.ExecuteWithError(cmd) tt.Log(output) require.NoError(tt, err, "agent installation should not return any error: ", err) From 2017a149b1d67ca609f2e54374b51028975121c0 Mon Sep 17 00:00:00 2001 From: Dinesh Gurumurthy Date: Wed, 29 Nov 2023 07:14:00 -0500 Subject: [PATCH 67/87] Convert `pkg/util/winutil` to module (#20851) * Convert pkg/util/winutil to module * create pkg/secrets module * Fix compiliation issues * go tidy * fix gofmt * remove un-needed files * add doc file * fix lint issues * Add deleted files back * deleted un-needed * PR feedback update codeowners * fix typo * apply suggestion for doc.go * Fix revive error * Fix compilation error * Fix revive error that I missed * Update test to handle new mod * fix lint + test run * remove pkg/secrets --------- Co-authored-by: Julien Lebot --- .github/CODEOWNERS | 2 + .../agentcrashdetectimpl/agentcrashdetect.go | 17 +++--- comp/checks/bundle_test.go | 2 +- go.mod | 2 + .../corechecks/system/cpu/cpu_windows.go | 2 +- .../corechecks/system/cpu/cpu_windows_test.go | 2 +- .../system/disk/iostats_pdh_windows.go | 2 +- .../system/disk/iostats_pdh_windows_test.go | 2 +- .../filehandles/file_handles_windows.go | 2 +- .../filehandles/file_handles_windows_test.go | 2 +- .../system/memory/memory_windows.go | 2 +- .../system/memory/memory_windows_test.go | 2 +- .../system/wincrashdetect/wincrashdetect.go | 7 ++- .../system/winproc/winproc_windows.go | 2 +- .../system/winproc/winproc_windows_test.go | 2 +- .../protocols/http/etw_http_service.go | 58 +++++++++++++++---- pkg/process/procutil/process_windows.go | 2 +- .../{winutil => }/crashreport/crashreport.go | 0 pkg/util/{winutil => }/pdhutil/README.md | 0 pkg/util/{winutil => }/pdhutil/pdh.go | 0 pkg/util/{winutil => }/pdhutil/pdh_386.go | 0 pkg/util/{winutil => }/pdhutil/pdh_amd64.go | 0 pkg/util/{winutil => }/pdhutil/pdhcounter.go | 0 .../{winutil => }/pdhutil/pdhformatter.go | 0 pkg/util/{winutil => }/pdhutil/pdhhelper.go | 4 ++ .../{winutil => }/pdhutil/pdhmocks_windows.go | 0 .../{winutil => }/pdhutil/testdata_windows.go | 0 pkg/util/winutil/doc.go | 8 +++ pkg/util/winutil/etw/etw-utils.go | 36 ------------ pkg/util/winutil/go.mod | 25 ++++++++ pkg/util/winutil/go.sum | 20 +++++++ tasks/agent.py | 8 ++- tasks/modules.py | 1 + 33 files changed, 137 insertions(+), 75 deletions(-) rename pkg/util/{winutil => }/crashreport/crashreport.go (100%) rename pkg/util/{winutil => }/pdhutil/README.md (100%) rename pkg/util/{winutil => }/pdhutil/pdh.go (100%) rename pkg/util/{winutil => }/pdhutil/pdh_386.go (100%) rename pkg/util/{winutil => }/pdhutil/pdh_amd64.go (100%) rename pkg/util/{winutil => }/pdhutil/pdhcounter.go (100%) rename pkg/util/{winutil => }/pdhutil/pdhformatter.go (100%) rename pkg/util/{winutil => }/pdhutil/pdhhelper.go (98%) rename pkg/util/{winutil => }/pdhutil/pdhmocks_windows.go (100%) rename pkg/util/{winutil => }/pdhutil/testdata_windows.go (100%) create mode 100644 pkg/util/winutil/doc.go create mode 100644 pkg/util/winutil/go.mod create mode 100644 pkg/util/winutil/go.sum diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 73614974f722e..1156c14c0b4ab 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -354,6 +354,8 @@ /pkg/util/cgroups/ @DataDog/container-integrations /pkg/util/retry/ @DataDog/container-integrations /pkg/util/intern/ @DataDog/ebpf-platform +/pkg/util/crashreport/ @DataDog/windows-kernel-integrations +/pkg/util/pdhutil/ @DataDog/windows-agent /pkg/util/winutil/ @DataDog/windows-agent /pkg/languagedetection @DataDog/processes @DataDog/universal-service-monitoring /pkg/logs/ @DataDog/agent-metrics-logs diff --git a/comp/checks/agentcrashdetect/agentcrashdetectimpl/agentcrashdetect.go b/comp/checks/agentcrashdetect/agentcrashdetectimpl/agentcrashdetect.go index 3a848f7a94710..aefdf616edc96 100644 --- a/comp/checks/agentcrashdetect/agentcrashdetectimpl/agentcrashdetect.go +++ b/comp/checks/agentcrashdetect/agentcrashdetectimpl/agentcrashdetect.go @@ -13,26 +13,23 @@ import ( "fmt" "strings" - "github.com/DataDog/datadog-agent/pkg/aggregator/sender" "go.uber.org/fx" + "golang.org/x/sys/windows/registry" + yaml "gopkg.in/yaml.v2" + "github.com/DataDog/datadog-agent/comp/checks/agentcrashdetect" + compsysconfig "github.com/DataDog/datadog-agent/comp/core/sysprobeconfig" + comptraceconfig "github.com/DataDog/datadog-agent/comp/trace/config" + "github.com/DataDog/datadog-agent/pkg/aggregator/sender" "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" - "github.com/DataDog/datadog-agent/pkg/collector/check" core "github.com/DataDog/datadog-agent/pkg/collector/corechecks" "github.com/DataDog/datadog-agent/pkg/collector/corechecks/system/wincrashdetect/probe" - - "github.com/DataDog/datadog-agent/comp/checks/agentcrashdetect" - compsysconfig "github.com/DataDog/datadog-agent/comp/core/sysprobeconfig" - comptraceconfig "github.com/DataDog/datadog-agent/comp/trace/config" - "github.com/DataDog/datadog-agent/pkg/internaltelemetry" traceconfig "github.com/DataDog/datadog-agent/pkg/trace/config" + "github.com/DataDog/datadog-agent/pkg/util/crashreport" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/log" - "github.com/DataDog/datadog-agent/pkg/util/winutil/crashreport" - "golang.org/x/sys/windows/registry" - yaml "gopkg.in/yaml.v2" ) const ( diff --git a/comp/checks/bundle_test.go b/comp/checks/bundle_test.go index 8e5b5c8d7b52c..b4441761ec286 100644 --- a/comp/checks/bundle_test.go +++ b/comp/checks/bundle_test.go @@ -12,8 +12,8 @@ import ( "github.com/DataDog/datadog-agent/comp/core" comptraceconfig "github.com/DataDog/datadog-agent/comp/trace/config" + "github.com/DataDog/datadog-agent/pkg/util/crashreport" "github.com/DataDog/datadog-agent/pkg/util/fxutil" - "github.com/DataDog/datadog-agent/pkg/util/winutil/crashreport" "go.uber.org/fx" ) diff --git a/go.mod b/go.mod index 640848fec748d..6ba699b70d071 100644 --- a/go.mod +++ b/go.mod @@ -63,6 +63,7 @@ replace ( github.com/DataDog/datadog-agent/pkg/util/statstracker => ./pkg/util/statstracker github.com/DataDog/datadog-agent/pkg/util/system/socket => ./pkg/util/system/socket/ github.com/DataDog/datadog-agent/pkg/util/testutil => ./pkg/util/testutil + github.com/DataDog/datadog-agent/pkg/util/winutil => ./pkg/util/winutil/ github.com/DataDog/datadog-agent/pkg/version => ./pkg/version ) @@ -611,6 +612,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/sort v0.50.0-rc.4 github.com/DataDog/datadog-agent/pkg/util/statstracker v0.50.0-rc.4 github.com/DataDog/datadog-agent/pkg/util/testutil v0.50.0-rc.4 + github.com/DataDog/datadog-agent/pkg/util/winutil v0.0.0-00010101000000-000000000000 github.com/DataDog/datadog-agent/pkg/version v0.50.0-rc.4 github.com/DataDog/go-libddwaf/v2 v2.2.0 github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.8.0 diff --git a/pkg/collector/corechecks/system/cpu/cpu_windows.go b/pkg/collector/corechecks/system/cpu/cpu_windows.go index 98028ffdcc84b..8e729527c56a8 100644 --- a/pkg/collector/corechecks/system/cpu/cpu_windows.go +++ b/pkg/collector/corechecks/system/cpu/cpu_windows.go @@ -21,7 +21,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/collector/check" core "github.com/DataDog/datadog-agent/pkg/collector/corechecks" "github.com/DataDog/datadog-agent/pkg/util/log" - "github.com/DataDog/datadog-agent/pkg/util/winutil/pdhutil" + "github.com/DataDog/datadog-agent/pkg/util/pdhutil" ) const cpuCheckName = "cpu" diff --git a/pkg/collector/corechecks/system/cpu/cpu_windows_test.go b/pkg/collector/corechecks/system/cpu/cpu_windows_test.go index a1c973a036324..57259500457a7 100644 --- a/pkg/collector/corechecks/system/cpu/cpu_windows_test.go +++ b/pkg/collector/corechecks/system/cpu/cpu_windows_test.go @@ -16,7 +16,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" gohaicpu "github.com/DataDog/datadog-agent/pkg/gohai/cpu" gohaiutils "github.com/DataDog/datadog-agent/pkg/gohai/utils" - pdhtest "github.com/DataDog/datadog-agent/pkg/util/winutil/pdhutil" + pdhtest "github.com/DataDog/datadog-agent/pkg/util/pdhutil" ) func CPUInfo() *gohaicpu.Info { diff --git a/pkg/collector/corechecks/system/disk/iostats_pdh_windows.go b/pkg/collector/corechecks/system/disk/iostats_pdh_windows.go index e24bf49d64ea0..fee24cc3f7dc5 100644 --- a/pkg/collector/corechecks/system/disk/iostats_pdh_windows.go +++ b/pkg/collector/corechecks/system/disk/iostats_pdh_windows.go @@ -17,7 +17,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" core "github.com/DataDog/datadog-agent/pkg/collector/corechecks" - "github.com/DataDog/datadog-agent/pkg/util/winutil/pdhutil" + "github.com/DataDog/datadog-agent/pkg/util/pdhutil" "golang.org/x/sys/windows" ) diff --git a/pkg/collector/corechecks/system/disk/iostats_pdh_windows_test.go b/pkg/collector/corechecks/system/disk/iostats_pdh_windows_test.go index f98c978cca0aa..24cb3828c33f4 100644 --- a/pkg/collector/corechecks/system/disk/iostats_pdh_windows_test.go +++ b/pkg/collector/corechecks/system/disk/iostats_pdh_windows_test.go @@ -13,7 +13,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" - pdhtest "github.com/DataDog/datadog-agent/pkg/util/winutil/pdhutil" + pdhtest "github.com/DataDog/datadog-agent/pkg/util/pdhutil" ) func testGetDriveType(drive string) uintptr { diff --git a/pkg/collector/corechecks/system/filehandles/file_handles_windows.go b/pkg/collector/corechecks/system/filehandles/file_handles_windows.go index 022b4266625bb..68028ff2ec7cc 100644 --- a/pkg/collector/corechecks/system/filehandles/file_handles_windows.go +++ b/pkg/collector/corechecks/system/filehandles/file_handles_windows.go @@ -11,7 +11,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/collector/check" core "github.com/DataDog/datadog-agent/pkg/collector/corechecks" - "github.com/DataDog/datadog-agent/pkg/util/winutil/pdhutil" + "github.com/DataDog/datadog-agent/pkg/util/pdhutil" ) const fileHandlesCheckName = "file_handle" diff --git a/pkg/collector/corechecks/system/filehandles/file_handles_windows_test.go b/pkg/collector/corechecks/system/filehandles/file_handles_windows_test.go index aabeead694568..6f338f31425a1 100644 --- a/pkg/collector/corechecks/system/filehandles/file_handles_windows_test.go +++ b/pkg/collector/corechecks/system/filehandles/file_handles_windows_test.go @@ -11,7 +11,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" - pdhtest "github.com/DataDog/datadog-agent/pkg/util/winutil/pdhutil" + pdhtest "github.com/DataDog/datadog-agent/pkg/util/pdhutil" ) func TestFhCheckWindows(t *testing.T) { diff --git a/pkg/collector/corechecks/system/memory/memory_windows.go b/pkg/collector/corechecks/system/memory/memory_windows.go index c8a74604af0a4..fc36d3279d38c 100644 --- a/pkg/collector/corechecks/system/memory/memory_windows.go +++ b/pkg/collector/corechecks/system/memory/memory_windows.go @@ -11,8 +11,8 @@ import ( "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" core "github.com/DataDog/datadog-agent/pkg/collector/corechecks" + "github.com/DataDog/datadog-agent/pkg/util/pdhutil" "github.com/DataDog/datadog-agent/pkg/util/winutil" - "github.com/DataDog/datadog-agent/pkg/util/winutil/pdhutil" ) // For testing purpose diff --git a/pkg/collector/corechecks/system/memory/memory_windows_test.go b/pkg/collector/corechecks/system/memory/memory_windows_test.go index db457ad58abdf..7c40e403e0448 100644 --- a/pkg/collector/corechecks/system/memory/memory_windows_test.go +++ b/pkg/collector/corechecks/system/memory/memory_windows_test.go @@ -14,8 +14,8 @@ import ( "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" + pdhtest "github.com/DataDog/datadog-agent/pkg/util/pdhutil" "github.com/DataDog/datadog-agent/pkg/util/winutil" - pdhtest "github.com/DataDog/datadog-agent/pkg/util/winutil/pdhutil" ) func VirtualMemory() (*winutil.VirtualMemoryStat, error) { diff --git a/pkg/collector/corechecks/system/wincrashdetect/wincrashdetect.go b/pkg/collector/corechecks/system/wincrashdetect/wincrashdetect.go index 664f3d1eadd98..f0451db62f855 100644 --- a/pkg/collector/corechecks/system/wincrashdetect/wincrashdetect.go +++ b/pkg/collector/corechecks/system/wincrashdetect/wincrashdetect.go @@ -11,16 +11,17 @@ package wincrashdetect import ( "fmt" + "golang.org/x/sys/windows/registry" + yaml "gopkg.in/yaml.v2" + "github.com/DataDog/datadog-agent/pkg/aggregator/sender" "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/collector/check" core "github.com/DataDog/datadog-agent/pkg/collector/corechecks" "github.com/DataDog/datadog-agent/pkg/collector/corechecks/system/wincrashdetect/probe" "github.com/DataDog/datadog-agent/pkg/metrics/event" + "github.com/DataDog/datadog-agent/pkg/util/crashreport" "github.com/DataDog/datadog-agent/pkg/util/log" - "github.com/DataDog/datadog-agent/pkg/util/winutil/crashreport" - "golang.org/x/sys/windows/registry" - yaml "gopkg.in/yaml.v2" ) const ( diff --git a/pkg/collector/corechecks/system/winproc/winproc_windows.go b/pkg/collector/corechecks/system/winproc/winproc_windows.go index 4896491707317..d4ca2b29c0163 100644 --- a/pkg/collector/corechecks/system/winproc/winproc_windows.go +++ b/pkg/collector/corechecks/system/winproc/winproc_windows.go @@ -11,7 +11,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/collector/check" core "github.com/DataDog/datadog-agent/pkg/collector/corechecks" - "github.com/DataDog/datadog-agent/pkg/util/winutil/pdhutil" + "github.com/DataDog/datadog-agent/pkg/util/pdhutil" ) const winprocCheckName = "winproc" diff --git a/pkg/collector/corechecks/system/winproc/winproc_windows_test.go b/pkg/collector/corechecks/system/winproc/winproc_windows_test.go index ad59684ed36a9..adf8cda4654a7 100644 --- a/pkg/collector/corechecks/system/winproc/winproc_windows_test.go +++ b/pkg/collector/corechecks/system/winproc/winproc_windows_test.go @@ -11,7 +11,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" - pdhtest "github.com/DataDog/datadog-agent/pkg/util/winutil/pdhutil" + pdhtest "github.com/DataDog/datadog-agent/pkg/util/pdhutil" ) func TestWinprocCheckWindows(t *testing.T) { diff --git a/pkg/network/protocols/http/etw_http_service.go b/pkg/network/protocols/http/etw_http_service.go index 61350e1ef1af5..6eeb9ba9badec 100644 --- a/pkg/network/protocols/http/etw_http_service.go +++ b/pkg/network/protocols/http/etw_http_service.go @@ -125,6 +125,8 @@ package http import ( "encoding/binary" "errors" + "fmt" + "net/netip" "net/url" "os" "strconv" @@ -329,8 +331,8 @@ func completeReqRespTracking(eventInfo *etw.DDEtwEventInfo, httpConnLink *HttpCo log.Infof(" ConnActivityId: %v\n", etw.FormatGUID(httpConnLink.connActivityId)) log.Infof(" ActivityId: %v\n", etw.FormatGUID(eventInfo.Event.ActivityId)) if connFound { - log.Infof(" Local: %v\n", etw.IPFormat(connOpen.conn.tup, true)) - log.Infof(" Remote: %v\n", etw.IPFormat(connOpen.conn.tup, false)) + log.Infof(" Local: %v\n", IPFormat(connOpen.conn.tup, true)) + log.Infof(" Remote: %v\n", IPFormat(connOpen.conn.tup, false)) log.Infof(" Family: %v\n", connOpen.conn.tup.Family) } log.Infof(" AppPool: %v\n", httpConnLink.http.AppPool) @@ -345,8 +347,8 @@ func completeReqRespTracking(eventInfo *etw.DDEtwEventInfo, httpConnLink *HttpCo log.Infof("%v. %v L[%v], R[%v], F[%v], P[%v], C[%v], V[%v], U[%v]\n", completedRequestCount, etw.FormatUnixTime(httpConnLink.http.Txn.RequestStarted), - etw.IPFormat(connOpen.conn.tup, true), - etw.IPFormat(connOpen.conn.tup, false), + IPFormat(connOpen.conn.tup, true), + IPFormat(connOpen.conn.tup, false), connOpen.conn.tup.Family, httpConnLink.http.AppPool, httpConnLink.http.Txn.ResponseStatusCode, @@ -504,8 +506,8 @@ func httpCallbackOnHTTPConnectionTraceTaskConnConn(eventInfo *etw.DDEtwEventInfo if HttpServiceLogVerbosity == HttpServiceLogVeryVerbose { log.Infof(" Time: %v\n", etw.FormatUnixTime(connOpen.conn.connected)) log.Infof(" ActivityId: %v\n", etw.FormatGUID(eventInfo.Event.ActivityId)) - log.Infof(" Local: %v\n", etw.IPFormat(connOpen.conn.tup, true)) - log.Infof(" Remote: %v\n", etw.IPFormat(connOpen.conn.tup, false)) + log.Infof(" Local: %v\n", IPFormat(connOpen.conn.tup, true)) + log.Infof(" Remote: %v\n", IPFormat(connOpen.conn.tup, false)) log.Infof(" Family: %v\n", connOpen.conn.tup.Family) log.Infof("\n") } @@ -542,8 +544,8 @@ func httpCallbackOnHTTPConnectionTraceTaskConnClose(eventInfo *etw.DDEtwEventInf if found { log.Infof(" ActivityId: %v, Local[%v], Remote[%v], Family[%v]\n", etw.FormatGUID(eventInfo.Event.ActivityId), - etw.IPFormat(connOpen.conn.tup, true), - etw.IPFormat(connOpen.conn.tup, false), + IPFormat(connOpen.conn.tup, true), + IPFormat(connOpen.conn.tup, false), connOpen.conn.tup.Family) } else { log.Infof(" ActivityId: %v not found\n", etw.FormatGUID(eventInfo.Event.ActivityId)) @@ -634,8 +636,8 @@ func httpCallbackOnHTTPRequestTraceTaskRecvReq(eventInfo *etw.DDEtwEventInfo) { log.Infof(" ActivityId: %v\n", etw.FormatGUID(eventInfo.Event.ActivityId)) log.Infof(" RelActivityId: %v\n", etw.FormatGUID(*eventInfo.RelatedActivityId)) if connFound { - log.Infof(" Local: %v\n", etw.IPFormat(connOpen.conn.tup, true)) - log.Infof(" Remote: %v\n", etw.IPFormat(connOpen.conn.tup, false)) + log.Infof(" Local: %v\n", IPFormat(connOpen.conn.tup, true)) + log.Infof(" Remote: %v\n", IPFormat(connOpen.conn.tup, false)) log.Infof(" Family: %v\n", connOpen.conn.tup.Family) } log.Infof("\n") @@ -804,8 +806,8 @@ func httpCallbackOnHTTPRequestTraceTaskDeliver(eventInfo *etw.DDEtwEventInfo) { log.Infof(" AppPool: %v\n", httpConnLink.http.AppPool) log.Infof(" Url: %v\n", httpConnLink.url) if connFound { - log.Infof(" Local: %v\n", etw.IPFormat(connOpen.conn.tup, true)) - log.Infof(" Remote: %v\n", etw.IPFormat(connOpen.conn.tup, false)) + log.Infof(" Local: %v\n", IPFormat(connOpen.conn.tup, true)) + log.Infof(" Remote: %v\n", IPFormat(connOpen.conn.tup, false)) log.Infof(" Family: %v\n", connOpen.conn.tup.Family) } log.Infof("\n") @@ -1345,3 +1347,35 @@ func (hei *EtwInterface) OnStop() { iisConfig = nil } } +func ipAndPortFromTup(tup driver.ConnTupleType, local bool) ([16]uint8, uint16) { + if local { + return tup.LocalAddr, tup.LocalPort + } + return tup.RemoteAddr, tup.RemotePort +} + +func ip4format(ip [16]uint8) string { + ipObj := netip.AddrFrom4(*(*[4]byte)(ip[:4])) + return ipObj.String() +} + +func ip6format(ip [16]uint8) string { + ipObj := netip.AddrFrom16(ip) + return ipObj.String() +} + +// IPFormat takes a binary ip representation and returns a string type +func IPFormat(tup driver.ConnTupleType, local bool) string { + ip, port := ipAndPortFromTup(tup, local) + + if tup.Family == 2 { + // IPv4 + return fmt.Sprintf("%v:%v", ip4format(ip), port) + } else if tup.Family == 23 { + // IPv6 + return fmt.Sprintf("[%v]:%v", ip6format(ip), port) + } else { + // everything else + return "" + } +} diff --git a/pkg/process/procutil/process_windows.go b/pkg/process/procutil/process_windows.go index 9598414f3c4a4..1661cdc954f6e 100644 --- a/pkg/process/procutil/process_windows.go +++ b/pkg/process/procutil/process_windows.go @@ -16,8 +16,8 @@ import ( "golang.org/x/sys/windows" "github.com/DataDog/datadog-agent/pkg/util/log" + "github.com/DataDog/datadog-agent/pkg/util/pdhutil" "github.com/DataDog/datadog-agent/pkg/util/winutil" - "github.com/DataDog/datadog-agent/pkg/util/winutil/pdhutil" ) var ( diff --git a/pkg/util/winutil/crashreport/crashreport.go b/pkg/util/crashreport/crashreport.go similarity index 100% rename from pkg/util/winutil/crashreport/crashreport.go rename to pkg/util/crashreport/crashreport.go diff --git a/pkg/util/winutil/pdhutil/README.md b/pkg/util/pdhutil/README.md similarity index 100% rename from pkg/util/winutil/pdhutil/README.md rename to pkg/util/pdhutil/README.md diff --git a/pkg/util/winutil/pdhutil/pdh.go b/pkg/util/pdhutil/pdh.go similarity index 100% rename from pkg/util/winutil/pdhutil/pdh.go rename to pkg/util/pdhutil/pdh.go diff --git a/pkg/util/winutil/pdhutil/pdh_386.go b/pkg/util/pdhutil/pdh_386.go similarity index 100% rename from pkg/util/winutil/pdhutil/pdh_386.go rename to pkg/util/pdhutil/pdh_386.go diff --git a/pkg/util/winutil/pdhutil/pdh_amd64.go b/pkg/util/pdhutil/pdh_amd64.go similarity index 100% rename from pkg/util/winutil/pdhutil/pdh_amd64.go rename to pkg/util/pdhutil/pdh_amd64.go diff --git a/pkg/util/winutil/pdhutil/pdhcounter.go b/pkg/util/pdhutil/pdhcounter.go similarity index 100% rename from pkg/util/winutil/pdhutil/pdhcounter.go rename to pkg/util/pdhutil/pdhcounter.go diff --git a/pkg/util/winutil/pdhutil/pdhformatter.go b/pkg/util/pdhutil/pdhformatter.go similarity index 100% rename from pkg/util/winutil/pdhutil/pdhformatter.go rename to pkg/util/pdhutil/pdhformatter.go diff --git a/pkg/util/winutil/pdhutil/pdhhelper.go b/pkg/util/pdhutil/pdhhelper.go similarity index 98% rename from pkg/util/winutil/pdhutil/pdhhelper.go rename to pkg/util/pdhutil/pdhhelper.go index 67b9457c8e806..ed5ad9c3a9b65 100644 --- a/pkg/util/winutil/pdhutil/pdhhelper.go +++ b/pkg/util/pdhutil/pdhhelper.go @@ -87,7 +87,9 @@ func refreshPdhObjectCache(forceRefresh bool) (didrefresh bool, err error) { // log errors and warnings but will decrease the time before the failing // counters have a chance to initialize successfully. + //revive:disable:redefines-builtin-id var len uint32 + //revive:enable:redefines-builtin-id refreshInterval := config.Datadog.GetInt("windows_counter_refresh_interval") if refreshInterval == 0 { @@ -191,7 +193,9 @@ func pdhMakeCounterPath(machine string, object string, instance string, counter if counter != "" { elems.countername = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(counter))) } + //revive:disable:redefines-builtin-id var len uint32 + //revive:enable:redefines-builtin-id r, _, _ := procPdhMakeCounterPath.Call( uintptr(unsafe.Pointer(&elems)), uintptr(0), diff --git a/pkg/util/winutil/pdhutil/pdhmocks_windows.go b/pkg/util/pdhutil/pdhmocks_windows.go similarity index 100% rename from pkg/util/winutil/pdhutil/pdhmocks_windows.go rename to pkg/util/pdhutil/pdhmocks_windows.go diff --git a/pkg/util/winutil/pdhutil/testdata_windows.go b/pkg/util/pdhutil/testdata_windows.go similarity index 100% rename from pkg/util/winutil/pdhutil/testdata_windows.go rename to pkg/util/pdhutil/testdata_windows.go diff --git a/pkg/util/winutil/doc.go b/pkg/util/winutil/doc.go new file mode 100644 index 0000000000000..c8dcb386d54ec --- /dev/null +++ b/pkg/util/winutil/doc.go @@ -0,0 +1,8 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2018-present Datadog, Inc. +//go:build !windows + +// Package winutil - Windows utility functions +package winutil diff --git a/pkg/util/winutil/etw/etw-utils.go b/pkg/util/winutil/etw/etw-utils.go index dfcbc1801e444..ea190cd395e7a 100644 --- a/pkg/util/winutil/etw/etw-utils.go +++ b/pkg/util/winutil/etw/etw-utils.go @@ -10,15 +10,12 @@ package etw import ( "bytes" "fmt" - "net/netip" "reflect" "strconv" "time" "unsafe" "golang.org/x/sys/windows" - - "github.com/DataDog/datadog-agent/pkg/network/driver" ) /* @@ -294,36 +291,3 @@ func skipASCIIString(data []byte, offset int) (nextOffset int, valFound bool, fo return (offset + termZeroIdx + 1), true, (offset + termZeroIdx + 1) } - -func ip4format(ip [16]uint8) string { - ipObj := netip.AddrFrom4(*(*[4]byte)(ip[:4])) - return ipObj.String() -} - -func ip6format(ip [16]uint8) string { - ipObj := netip.AddrFrom16(ip) - return ipObj.String() -} - -func ipAndPortFromTup(tup driver.ConnTupleType, local bool) ([16]uint8, uint16) { - if local { - return tup.LocalAddr, tup.LocalPort - } - return tup.RemoteAddr, tup.RemotePort -} - -// IPFormat takes a binary ip representation and returns a string type -func IPFormat(tup driver.ConnTupleType, local bool) string { - ip, port := ipAndPortFromTup(tup, local) - - if tup.Family == 2 { - // IPv4 - return fmt.Sprintf("%v:%v", ip4format(ip), port) - } else if tup.Family == 23 { - // IPv6 - return fmt.Sprintf("[%v]:%v", ip6format(ip), port) - } else { - // everything else - return "" - } -} diff --git a/pkg/util/winutil/go.mod b/pkg/util/winutil/go.mod new file mode 100644 index 0000000000000..cc15cbb63a824 --- /dev/null +++ b/pkg/util/winutil/go.mod @@ -0,0 +1,25 @@ +module github.com/DataDog/datadog-agent/pkg/util/winutil + +go 1.20 + +replace ( + github.com/DataDog/datadog-agent/pkg/util/log => ../log/ + github.com/DataDog/datadog-agent/pkg/util/scrubber => ../scrubber/ +) + +require ( + github.com/DataDog/datadog-agent/pkg/util/log v0.0.0-00010101000000-000000000000 + github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 + github.com/fsnotify/fsnotify v1.7.0 + github.com/stretchr/testify v1.8.4 + go.uber.org/atomic v1.11.0 + golang.org/x/sys v0.14.0 +) + +require ( + github.com/DataDog/datadog-agent/pkg/util/scrubber v0.50.0-rc.4 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/pkg/util/winutil/go.sum b/pkg/util/winutil/go.sum new file mode 100644 index 0000000000000..c25f42a430cbe --- /dev/null +++ b/pkg/util/winutil/go.sum @@ -0,0 +1,20 @@ +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tasks/agent.py b/tasks/agent.py index 5bf5cd6ddf175..35dc52fe29ab3 100644 --- a/tasks/agent.py +++ b/tasks/agent.py @@ -493,23 +493,27 @@ def _windows_integration_tests(ctx, race=False, go_mod="mod", arch="x64"): tests = [ { # Run eventlog tests with the Windows API, which depend on the EventLog service - 'prefix': './pkg/util/winutil/eventlog/...', + "dir": "./pkg/util/winutil/", + 'prefix': './eventlog/...', 'extra_args': '-evtapi Windows', }, { # Run eventlog tailer tests with the Windows API, which depend on the EventLog service + "dir": ".", 'prefix': './pkg/logs/tailers/windowsevent/...', 'extra_args': '-evtapi Windows', }, { # Run eventlog check tests with the Windows API, which depend on the EventLog service + "dir": ".", 'prefix': './pkg/collector/corechecks/windows_event_log/...', 'extra_args': '-evtapi Windows', }, ] for test in tests: - ctx.run(f"{go_cmd} {test['prefix']} {test['extra_args']}") + with ctx.cd(f"{test['dir']}"): + ctx.run(f"{go_cmd} {test['prefix']} {test['extra_args']}") def _linux_integration_tests(ctx, race=False, remote_docker=False, go_mod="mod", arch="x64"): diff --git a/tasks/modules.py b/tasks/modules.py index 9d1a26ce7757d..47a89de08251f 100644 --- a/tasks/modules.py +++ b/tasks/modules.py @@ -186,6 +186,7 @@ def dependency_path(self, agent_version): "pkg/util/statstracker": GoModule("pkg/util/statstracker", independent=True), "pkg/util/system/socket": GoModule("pkg/util/system/socket", independent=True), "pkg/util/testutil": GoModule("pkg/util/testutil", independent=True), + "pkg/util/winutil": GoModule("pkg/util/winutil", independent=True), "pkg/version": GoModule("pkg/version", independent=True), "pkg/networkdevice/profile": GoModule("pkg/networkdevice/profile", independent=True), "pkg/collector/check/defaults": GoModule("pkg/collector/check/defaults", independent=True), From 0a949b67ead4af51c308db89e71b34d5a4b4282b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9na=C3=AFc=20Huard?= Date: Wed, 29 Nov 2023 14:34:27 +0100 Subject: [PATCH 68/87] Revert "Remove AWS from disable default providers until EKS plugin is fixed (#17787)" (#21173) Revert "Remove AWS from disable default providers until EKS plugin is fixed (#17787)" --- .gitlab-ci.yml | 2 +- test/new-e2e/go.mod | 2 +- test/new-e2e/go.sum | 4 ++-- test/new-e2e/pkg/utils/infra/stack_manager.go | 9 ++------- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e814cbe8fe239..f515728354853 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -177,7 +177,7 @@ variables: # To use images from test-infra-definitions dev branches, set the SUFFIX variable to -dev # and check the job creating the image to make sure you have the right SHA prefix TEST_INFRA_DEFINITIONS_BUILDIMAGES_SUFFIX: "" - TEST_INFRA_DEFINITIONS_BUILDIMAGES: 4650ea56cf36 + TEST_INFRA_DEFINITIONS_BUILDIMAGES: a60850d5606e DATADOG_AGENT_BUILDERS: v22276738-b36b132 DATADOG_AGENT_EMBEDDED_PATH: /opt/datadog-agent/embedded diff --git a/test/new-e2e/go.mod b/test/new-e2e/go.mod index 279bfa5d4a437..f7ec0801f565b 100644 --- a/test/new-e2e/go.mod +++ b/test/new-e2e/go.mod @@ -22,7 +22,7 @@ require ( // `TEST_INFRA_DEFINITIONS_BUILDIMAGES` matches the commit sha in the module version // Example: github.com/DataDog/test-infra-definitions v0.0.0-YYYYMMDDHHmmSS-0123456789AB // => TEST_INFRA_DEFINITIONS_BUILDIMAGES: 0123456789AB - github.com/DataDog/test-infra-definitions v0.0.0-20231127165329-4650ea56cf36 + github.com/DataDog/test-infra-definitions v0.0.0-20231129092443-a60850d5606e github.com/aws/aws-sdk-go-v2 v1.22.1 github.com/aws/aws-sdk-go-v2/config v1.18.40 github.com/aws/aws-sdk-go-v2/service/ec2 v1.130.0 diff --git a/test/new-e2e/go.sum b/test/new-e2e/go.sum index 7023952f8819b..c5f52da5f7155 100644 --- a/test/new-e2e/go.sum +++ b/test/new-e2e/go.sum @@ -12,8 +12,8 @@ github.com/DataDog/datadog-api-client-go/v2 v2.15.0 h1:5UVON1xs6Lul4d6R5TmLDqqSJ github.com/DataDog/datadog-api-client-go/v2 v2.15.0/go.mod h1:ZG8wS+y2rUmkRDJZQq7Og7EAPFPage+7vXcmuah2I9o= github.com/DataDog/mmh3 v0.0.0-20200805151601-30884ca2197a h1:m9REhmyaWD5YJ0P53ygRHxKKo+KM+nw+zz0hEdKztMo= github.com/DataDog/mmh3 v0.0.0-20200805151601-30884ca2197a/go.mod h1:SvsjzyJlSg0rKsqYgdcFxeEVflx3ZNAyFfkUHP0TxXg= -github.com/DataDog/test-infra-definitions v0.0.0-20231127165329-4650ea56cf36 h1:eidj3lBZ7wvGdVCvr7MlZ9FcdTFpj8C4yBkh5qtI4gU= -github.com/DataDog/test-infra-definitions v0.0.0-20231127165329-4650ea56cf36/go.mod h1:P6FDz6Iyki8kX1WoDln5U+3kVLC+QzRi7FIzauuZ6jM= +github.com/DataDog/test-infra-definitions v0.0.0-20231129092443-a60850d5606e h1:yVjfUFnwtxqppiAnlQbQcGt6wQ9hqaRoI72+HP690OE= +github.com/DataDog/test-infra-definitions v0.0.0-20231129092443-a60850d5606e/go.mod h1:P6FDz6Iyki8kX1WoDln5U+3kVLC+QzRi7FIzauuZ6jM= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/DataDog/zstd_0 v0.0.0-20210310093942-586c1286621f h1:5Vuo4niPKFkfwW55jV4vY0ih3VQ9RaQqeqY67fvRn8A= diff --git a/test/new-e2e/pkg/utils/infra/stack_manager.go b/test/new-e2e/pkg/utils/infra/stack_manager.go index ae79321270848..8f12645b53e53 100644 --- a/test/new-e2e/pkg/utils/infra/stack_manager.go +++ b/test/new-e2e/pkg/utils/infra/stack_manager.go @@ -248,14 +248,9 @@ func buildWorkspace(ctx context.Context, profile runner.Profile, stackName strin Description: pulumi.StringRef("E2E Test inline project"), StackConfigDir: stackName, Config: map[string]workspace.ProjectConfigType{ - // We should always disable default providers - // Disabling all known except AWS due to https://github.com/pulumi/pulumi-eks/pull/886 + // Always disable "pulumi:disable-default-providers": { - Value: []string{"kubernetes", "azure-native", "awsx", "eks"}, - }, - // Required in CI due to https://github.com/pulumi/pulumi-eks/pull/886 - "aws:skipMetadataApiCheck": { - Value: "false", + Value: []string{"*"}, }, }, } From 18a7f5ce96fd0b4353103b3d63ca38f9697268d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9na=C3=AFc=20Huard?= Date: Wed, 29 Nov 2023 14:39:54 +0100 Subject: [PATCH 69/87] =?UTF-8?q?[CONTINT-3410]=20Fix=20the=20=E2=80=A6=5F?= =?UTF-8?q?pods=5Fare=5Frunning=5Fthe=5Fgood=5Fversion=20e2e=20tests=20(#2?= =?UTF-8?q?1155)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [CONTINT-3410] Fix the …_pods_are_running_the_good_version e2e tests --- test/new-e2e/tests/containers/k8s_test.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/new-e2e/tests/containers/k8s_test.go b/test/new-e2e/tests/containers/k8s_test.go index d35671279e7da..452a37c0965b7 100644 --- a/test/new-e2e/tests/containers/k8s_test.go +++ b/test/new-e2e/tests/containers/k8s_test.go @@ -218,10 +218,15 @@ func (suite *k8sSuite) TestVersion() { suite.Emptyf(stderr, "Standard error of `agent version` should be empty,") match := versionExtractor.FindStringSubmatch(stdout) if suite.Equalf(2, len(match), "'Commit' not found in the output of `agent version`.") { - if len(GitCommit) == 10 && len(match[1]) == 7 { - suite.Equalf(GitCommit[:7], match[1], "Agent isn’t running the expected version") - } else { - suite.Equalf(GitCommit, match[1], "Agent isn’t running the expected version") + if suite.Greaterf(len(GitCommit), 6, "Couldn’t guess the expected version of the agent.") && + suite.Greaterf(len(match[1]), 6, "Couldn’t find the version of the agent.") { + + size2compare := len(GitCommit) + if len(match[1]) < size2compare { + size2compare = len(match[1]) + } + + suite.Equalf(GitCommit[:size2compare], match[1][:size2compare], "Agent isn’t running the expected version") } } } From c58154604454c79cee30085cc5fc58268150d004 Mon Sep 17 00:00:00 2001 From: Alex Lopez Date: Wed, 29 Nov 2023 14:55:39 +0100 Subject: [PATCH 70/87] Merge software definitions for integration installation for py2 and py3 (#21041) --- .../remove-maxfile-maxpath-psutil.patch | 30 - .../create-regex-at-runtime.patch | 0 .../remove-maxfile-maxpath-psutil.patch | 0 omnibus/config/projects/agent.rb | 6 +- .../datadog-agent-integrations-py2.rb | 460 --------------- .../datadog-agent-integrations-py3.rb | 490 ---------------- .../software/datadog-agent-integrations.rb | 524 ++++++++++++++++++ .../static_requirements.txt.erb | 3 - .../static_requirements.txt.erb | 0 9 files changed, 527 insertions(+), 986 deletions(-) delete mode 100644 omnibus/config/patches/datadog-agent-integrations-py3/remove-maxfile-maxpath-psutil.patch rename omnibus/config/patches/{datadog-agent-integrations-py2 => datadog-agent-integrations}/create-regex-at-runtime.patch (100%) rename omnibus/config/patches/{datadog-agent-integrations-py2 => datadog-agent-integrations}/remove-maxfile-maxpath-psutil.patch (100%) delete mode 100644 omnibus/config/software/datadog-agent-integrations-py2.rb delete mode 100644 omnibus/config/software/datadog-agent-integrations-py3.rb create mode 100644 omnibus/config/software/datadog-agent-integrations.rb delete mode 100644 omnibus/config/templates/datadog-agent-integrations-py3/static_requirements.txt.erb rename omnibus/config/templates/{datadog-agent-integrations-py2 => datadog-agent-integrations}/static_requirements.txt.erb (100%) diff --git a/omnibus/config/patches/datadog-agent-integrations-py3/remove-maxfile-maxpath-psutil.patch b/omnibus/config/patches/datadog-agent-integrations-py3/remove-maxfile-maxpath-psutil.patch deleted file mode 100644 index c92eb2f7f2859..0000000000000 --- a/omnibus/config/patches/datadog-agent-integrations-py3/remove-maxfile-maxpath-psutil.patch +++ /dev/null @@ -1,30 +0,0 @@ -Partially reverts https://github.com/giampaolo/psutil/pull/1863 to remove the maxpath / maxfile fetch -diff --git a/psutil/__init__.py b/psutil/__init__.py -index 1a113bc3..ce962a61 100644 ---- a/psutil/__init__.py -+++ b/psutil/__init__.py -@@ -2012,23 +2012,7 @@ def disk_partitions(all=False): - If *all* parameter is False return physical devices only and ignore - all others. - """ -- def pathconf(path, name): -- try: -- return os.pathconf(path, name) -- except (OSError, AttributeError): -- pass -- -- ret = _psplatform.disk_partitions(all) -- if POSIX: -- new = [] -- for item in ret: -- nt = item._replace( -- maxfile=pathconf(item.mountpoint, 'PC_NAME_MAX'), -- maxpath=pathconf(item.mountpoint, 'PC_PATH_MAX')) -- new.append(nt) -- return new -- else: -- return ret -+ return _psplatform.disk_partitions(all) - - - def disk_io_counters(perdisk=False, nowrap=True): \ No newline at end of file diff --git a/omnibus/config/patches/datadog-agent-integrations-py2/create-regex-at-runtime.patch b/omnibus/config/patches/datadog-agent-integrations/create-regex-at-runtime.patch similarity index 100% rename from omnibus/config/patches/datadog-agent-integrations-py2/create-regex-at-runtime.patch rename to omnibus/config/patches/datadog-agent-integrations/create-regex-at-runtime.patch diff --git a/omnibus/config/patches/datadog-agent-integrations-py2/remove-maxfile-maxpath-psutil.patch b/omnibus/config/patches/datadog-agent-integrations/remove-maxfile-maxpath-psutil.patch similarity index 100% rename from omnibus/config/patches/datadog-agent-integrations-py2/remove-maxfile-maxpath-psutil.patch rename to omnibus/config/patches/datadog-agent-integrations/remove-maxfile-maxpath-psutil.patch diff --git a/omnibus/config/projects/agent.rb b/omnibus/config/projects/agent.rb index 2384e20f0cd00..6b77898b6d3f2 100644 --- a/omnibus/config/projects/agent.rb +++ b/omnibus/config/projects/agent.rb @@ -182,13 +182,13 @@ if with_python_runtime? "2" dependency 'pylint2' - dependency 'datadog-agent-integrations-py2' end -if with_python_runtime? "3" - dependency 'datadog-agent-integrations-py3' +if with_python_runtime? "3" or with_python_runtime? "2" + dependency 'datadog-agent-integrations' end + if linux_target? dependency 'datadog-security-agent-policies' end diff --git a/omnibus/config/software/datadog-agent-integrations-py2.rb b/omnibus/config/software/datadog-agent-integrations-py2.rb deleted file mode 100644 index ff62baa97b2cf..0000000000000 --- a/omnibus/config/software/datadog-agent-integrations-py2.rb +++ /dev/null @@ -1,460 +0,0 @@ -# Unless explicitly stated otherwise all files in this repository are licensed -# under the Apache License Version 2.0. -# This product includes software developed at Datadog (https:#www.datadoghq.com/). -# Copyright 2016-present Datadog, Inc. - -require './lib/ostools.rb' -require 'json' - -name 'datadog-agent-integrations-py2' - -license "BSD-3-Clause" -license_file "./LICENSE" - -dependency 'datadog-agent' -dependency 'datadog-agent-integrations-py2-dependencies' - -relative_path 'integrations-core' -whitelist_file "embedded/lib/python2.7/site-packages/.libsaerospike" -whitelist_file "embedded/lib/python2.7/site-packages/psycopg2" -whitelist_file "embedded/lib/python2.7/site-packages/wrapt" -whitelist_file "embedded/lib/python2.7/site-packages/pymqi" - -source git: 'https://github.com/DataDog/integrations-core.git' - -integrations_core_version = ENV['INTEGRATIONS_CORE_VERSION'] -if integrations_core_version.nil? || integrations_core_version.empty? - integrations_core_version = 'master' -end -default_version integrations_core_version - -# folder names containing integrations from -core that won't be packaged with the Agent -excluded_folders = [ - 'datadog_checks_base', # namespacing package for wheels (NOT AN INTEGRATION) - 'datadog_checks_dev', # Development package, (NOT AN INTEGRATION) - 'datadog_checks_tests_helper', # Testing and Development package, (NOT AN INTEGRATION) - 'docker_daemon', # Agent v5 only -] - -# package names of dependencies that won't be added to the Agent Python environment -excluded_packages = Array.new - - -if suse_target? - # Temporarily exclude Aerospike until builder supports new dependency - excluded_packages.push(/^aerospike==/) - excluded_folders.push('aerospike') -end - -if osx_target? - # exclude aerospike, new version 3.10 is not supported on MacOS yet - excluded_folders.push('aerospike') - - # Temporarily exclude Aerospike until builder supports new dependency - excluded_packages.push(/^aerospike==/) - excluded_folders.push('aerospike') -end - -if arm_target? - # Temporarily exclude Aerospike until builder supports new dependency - excluded_folders.push('aerospike') - excluded_packages.push(/^aerospike==/) - - # This doesn't build on ARM - excluded_folders.push('ibm_mq') - excluded_packages.push(/^pymqi==/) -end - -if arm_target? || !_64_bit? - excluded_packages.push(/^orjson==/) -end - -if linux_target? - excluded_packages.push(/^pyyaml==/) - excluded_packages.push(/^kubernetes==/) -end - -final_constraints_file = 'final_constraints-py2.txt' -agent_requirements_file = 'agent_requirements-py2.txt' -filtered_agent_requirements_in = 'agent_requirements-py2.in' -agent_requirements_in = 'agent_requirements.in' - -build do - # The dir for confs - if osx_target? - conf_dir = "#{install_dir}/etc/conf.d" - else - conf_dir = "#{install_dir}/etc/datadog-agent/conf.d" - end - mkdir conf_dir - - # aliases for pip - if windows_target? - pip = "#{windows_safe_path(python_2_embedded)}\\Scripts\\pip.exe" - python = "#{windows_safe_path(python_2_embedded)}\\python.exe" - else - pip = "#{install_dir}/embedded/bin/pip2" - python = "#{install_dir}/embedded/bin/python2" - end - - # If a python_mirror was set, it's passed through a pip config file so that we're not leaking the API key in the CI Output - # Else the pip config file so pip will act casually - pip_config_file = ENV['PIP_CONFIG_FILE'] - pre_build_env = { - "PIP_CONFIG_FILE" => "#{pip_config_file}" - } - - # Install the checks along with their dependencies - if windows_target? - wheel_build_dir = "#{windows_safe_path(project_dir)}\\.wheels" - build_deps_dir = "#{windows_safe_path(project_dir)}\\.build_deps" - else - wheel_build_dir = "#{project_dir}/.wheels" - build_deps_dir = "#{project_dir}/.build_deps" - end - - # - # Prepare the build env, these dependencies are only needed to build and - # install the core integrations. - # - command "#{pip} download --dest #{build_deps_dir} hatchling==0.25.1", :env => pre_build_env - command "#{pip} download --dest #{build_deps_dir} setuptools==40.9.0", :env => pre_build_env # Version from ./setuptools2.rb - command "#{pip} install wheel==0.37.1", :env => pre_build_env # Pin to the last version that supports Python 2 - command "#{pip} install setuptools-scm==5.0.2", :env => pre_build_env # Pin to the last version that supports Python 2 - command "#{pip} install pip-tools==5.4.0", :env => pre_build_env - uninstall_buildtime_deps = ['rtloader', 'click', 'first', 'pip-tools'] - nix_build_env = { - "PIP_FIND_LINKS" => "#{build_deps_dir}", - "PIP_CONFIG_FILE" => "#{pip_config_file}", - "CFLAGS" => "-I#{install_dir}/embedded/include -I/opt/mqm/inc", - "CXXFLAGS" => "-I#{install_dir}/embedded/include -I/opt/mqm/inc", - "LDFLAGS" => "-L#{install_dir}/embedded/lib -L/opt/mqm/lib64 -L/opt/mqm/lib", - "LD_RUN_PATH" => "#{install_dir}/embedded/lib -L/opt/mqm/lib64 -L/opt/mqm/lib", - "PATH" => "#{install_dir}/embedded/bin:#{ENV['PATH']}", - } - win_build_env = { - "PIP_FIND_LINKS" => "#{build_deps_dir}", - "PIP_CONFIG_FILE" => "#{pip_config_file}", - } - # Some libraries (looking at you, aerospike-client-python) need EXT_CFLAGS instead of CFLAGS. - nix_specific_build_env = { - "aerospike" => nix_build_env.merge({"EXT_CFLAGS" => nix_build_env["CFLAGS"] + " -std=gnu99"}), - # Always build pyodbc from source to link to the embedded version of libodbc - "pyodbc" => nix_build_env.merge({"PIP_NO_BINARY" => "pyodbc"}), - } - win_specific_build_env = {} - - - # On Linux & Windows, specify the C99 standard explicitly to avoid issues while building some - # wheels (eg. ddtrace). - # Not explicitly setting that option has caused us problems in the past on SUSE, where the ddtrace - # wheel has to be manually built, as the C code in ddtrace doesn't follow the C89 standard (the default value of std). - # Note: We don't set this on MacOS, as on MacOS we need to build a bunch of packages & C extensions that - # don't have precompiled MacOS wheels. When building C extensions, the CFLAGS variable is added to - # the command-line parameters, even when compiling C++ code, where -std=c99 is invalid. - # See: https://github.com/python/cpython/blob/v2.7.18/Lib/distutils/sysconfig.py#L222 - if linux_target? || windows_target? - nix_build_env["CFLAGS"] += " -std=c99" - end - - # - # Prepare the requirements file containing ALL the dependencies needed by - # any integration. This will provide the "static Python environment" of the Agent. - # We don't use the .in file provided by the base check directly because we - # want to filter out things before installing. - # - if windows_target? - static_reqs_in_file = "#{windows_safe_path(project_dir)}\\datadog_checks_base\\datadog_checks\\base\\data\\#{agent_requirements_in}" - static_reqs_out_folder = "#{windows_safe_path(project_dir)}\\" - static_reqs_out_file = static_reqs_out_folder + filtered_agent_requirements_in - compiled_reqs_file_path = "#{windows_safe_path(install_dir)}\\#{agent_requirements_file}" - else - static_reqs_in_file = "#{project_dir}/datadog_checks_base/datadog_checks/base/data/#{agent_requirements_in}" - static_reqs_out_folder = "#{project_dir}/" - static_reqs_out_file = static_reqs_out_folder + filtered_agent_requirements_in - compiled_reqs_file_path = "#{install_dir}/#{agent_requirements_file}" - end - - specific_build_env = windows_target? ? win_specific_build_env : nix_specific_build_env - build_env = windows_target? ? win_build_env : nix_build_env - cwd = windows_target? ? "#{windows_safe_path(project_dir)}\\datadog_checks_base" : "#{project_dir}/datadog_checks_base" - - # Creating a hash containing the requirements and requirements file path associated to every lib - requirements_custom = Hash.new() - specific_build_env.each do |lib, env| - lib_compiled_req_file_path = (windows_target? ? "#{windows_safe_path(install_dir)}\\" : "#{install_dir}/") + "agent_#{lib}_requirements-py2.txt" - requirements_custom[lib] = { - "req_lines" => Array.new, - "req_file_path" => static_reqs_out_folder + lib + "-py2.in", - "compiled_req_file_path" => lib_compiled_req_file_path, - } - end - - # Remove any excluded requirements from the static-environment req file - requirements = Array.new - - block "Create filtered requirements" do - File.open("#{static_reqs_in_file}", 'r+').readlines().each do |line| - next if excluded_packages.any? { |package_regex| line.match(package_regex) } - - if line.start_with?('psycopg[binary]') && !windows_target? - line.sub! 'psycopg[binary]', 'psycopg[c]' - end - # Keeping the custom env requirements lines apart to install them with a specific env - requirements_custom.each do |lib, lib_req| - if Regexp.new('^' + lib + '==').freeze.match line - lib_req["req_lines"].push(line) - end - end - # In any case we add the lib to the requirements files to avoid inconsistency in the installed versions - # For example if aerospike has dependency A>1.2.3 and a package in the big requirements file has A<1.2.3, the install process would succeed but the integration wouldn't work. - requirements.push(line) - end - - # Adding pympler for memory debug purposes - requirements.push("pympler==0.7") - - end - - # Render the filtered requirements file - erb source: "static_requirements.txt.erb", - dest: "#{static_reqs_out_file}", - mode: 0640, - vars: { requirements: requirements } - - # Render the filtered libraries that are to be built with different env var - requirements_custom.each do |lib, lib_req| - erb source: "static_requirements.txt.erb", - dest: "#{lib_req["req_file_path"]}", - mode: 0640, - vars: { requirements: lib_req["req_lines"] } - end - - # Increasing pip max retries (default: 5 times) and pip timeout (default 15 seconds) to avoid blocking network errors - pip_max_retries = 20 - pip_timeout = 20 - - # Use pip-compile to create the final requirements file. Notice when we invoke `pip` through `python -m pip <...>`, - # there's no need to refer to `pip`, the interpreter will pick the right script. - command "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => build_env, :cwd => cwd - command "#{python} -m pip install datadog_checks_base --no-deps --no-index --find-links=#{wheel_build_dir}" - command "#{python} -m piptools compile --generate-hashes --output-file #{compiled_reqs_file_path} #{static_reqs_out_file} " \ - "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => build_env - # Pip-compiling seperately each lib that needs a custom build installation - specific_build_env.each do |lib, env| - command "#{python} -m piptools compile --generate-hashes --output-file #{requirements_custom[lib]["compiled_req_file_path"]} #{requirements_custom[lib]["req_file_path"]} " \ - "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => env - end - - # - # Install static-environment requirements that the Agent and all checks will use - # - - # First we install the dependencies that need specific flags - specific_build_env.each do |lib, env| - command "#{python} -m pip install --no-deps --require-hashes -r #{requirements_custom[lib]["compiled_req_file_path"]}", :env => env - # Remove the file after use so it is not shipped - delete "#{requirements_custom[lib]["compiled_req_file_path"]}" - end - - # Then we install the rest (already installed libraries will be ignored) with the main flags - command "#{python} -m pip install --no-deps --require-hashes -r #{compiled_reqs_file_path}", :env => build_env - # Remove the file after use so it is not shipped - delete "#{compiled_reqs_file_path}" - - # - # Install Core integrations - # - - # Create a constraint file after installing all the core dependencies and before any integration - # This is then used as a constraint file by the integration command to avoid messing with the agent's python environment - command "#{pip} freeze > #{install_dir}/#{final_constraints_file}" - - if windows_target? - cached_wheels_dir = "#{windows_safe_path(wheel_build_dir)}\\.cached" - else - cached_wheels_dir = "#{wheel_build_dir}/.cached" - end - - checks_to_install = Array.new - - block "Collect integrations to install" do - # Go through every integration package in `integrations-core`, build and install - Dir.glob("#{project_dir}/*").each do |check_dir| - check = check_dir.split('/').last - - # do not install excluded integrations - next if !File.directory?("#{check_dir}") || excluded_folders.include?(check) - - # If there is no manifest file, then we should assume the folder does not - # contain a working check and move onto the next - manifest_file_path = "#{check_dir}/manifest.json" - - # If there is no manifest file, then we should assume the folder does not - # contain a working check and move onto the next - File.exist?(manifest_file_path) || next - - manifest = JSON.parse(File.read(manifest_file_path)) - if manifest.key?("supported_os") - manifest["supported_os"].include?(os) || next - else - if os == "mac_os" - tag = "Supported OS::macOS" - else - tag = "Supported OS::#{os.capitalize}" - end - - manifest["tile"]["classifier_tags"].include?(tag) || next - end - - File.file?("#{check_dir}/setup.py") || File.file?("#{check_dir}/pyproject.toml") || next - # Check if it supports Python 2. - support = `inv agent.check-supports-python-version #{check_dir} 2` - if support == "False" - log.info(log_key) { "Skipping '#{check}' since it does not support Python 2." } - next - end - - checks_to_install.push(check) - end - end - - installed_list = Array.new - cache_bucket = ENV.fetch('INTEGRATION_WHEELS_CACHE_BUCKET', '') - block "Install integrations" do - tasks_dir_in = windows_safe_path(Dir.pwd) - cache_branch = (shellout! "inv release.get-release-json-value base_branch", cwd: File.expand_path('..', tasks_dir_in)).stdout.strip - # On windows, `aws` actually executes Ruby's AWS SDK, but we want the Python one - awscli = if windows_target? then '"c:\Program files\python39\scripts\aws"' else 'aws' end - if cache_bucket != '' - mkdir cached_wheels_dir - shellout! "inv -e agent.get-integrations-from-cache " \ - "--python 2 --bucket #{cache_bucket} " \ - "--branch #{cache_branch || 'main'} " \ - "--integrations-dir #{windows_safe_path(project_dir)} " \ - "--target-dir #{cached_wheels_dir} " \ - "--integrations #{checks_to_install.join(',')} " \ - "--awscli #{awscli}", - :cwd => tasks_dir_in - - # install all wheels from cache in one pip invocation to speed things up - if windows_target? - shellout! "#{python} -m pip install --no-deps --no-index " \ - "--find-links #{windows_safe_path(cached_wheels_dir)} -r #{windows_safe_path(cached_wheels_dir)}\\found.txt" - else - shellout! "#{pip} install --no-deps --no-index " \ - " --find-links #{cached_wheels_dir} -r #{cached_wheels_dir}/found.txt" - end - end - - # get list of integration wheels already installed from cache - if cache_bucket != '' - if windows_target? - installed_out = (shellout! "#{python} -m pip list --format json").stdout - else - installed_out = (shellout! "#{pip} list --format json").stdout - end - if $?.exitstatus == 0 - installed = JSON.parse(installed_out) - installed.each do |package| - package.each do |key, value| - if key == "name" && value.start_with?("datadog-") - installed_list.push(value["datadog-".length..-1]) - end - end - end - else - raise "Failed to list pip installed packages" - end - end - - checks_to_install.each do |check| - check_dir = File.join(project_dir, check) - check_conf_dir = "#{conf_dir}/#{check}.d" - - # For each conf file, if it already exists, that means the `datadog-agent` software def - # wrote it first. In that case, since the agent's confs take precedence, skip the conf - conf_files = ["conf.yaml.example", "conf.yaml.default", "metrics.yaml", "auto_conf.yaml"] - - conf_files.each do |filename| - src = windows_safe_path(check_dir,"datadog_checks", check, "data", filename) - dest = check_conf_dir - if File.exist?(src) and !File.exist?(windows_safe_path(dest, filename)) - FileUtils.mkdir_p(dest) - FileUtils.cp_r(src, dest) - end - end - - # Copy SNMP profiles - profile_folders = ['profiles', 'default_profiles'] - profile_folders.each do |profile_folder| - folder_path = "#{check_dir}/datadog_checks/#{check}/data/#{profile_folder}" - if File.exist? folder_path - FileUtils.cp_r folder_path, "#{check_conf_dir}/" - end - end - - # pip < 21.2 replace underscores by dashes in package names per https://pip.pypa.io/en/stable/news/#v21-2 - # whether or not this might switch back in the future is not guaranteed, so we check for both name - # with dashes and underscores - if installed_list.include?(check) || installed_list.include?(check.gsub('_', '-')) - next - end - - if windows_target? - shellout! "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => win_build_env, :cwd => "#{windows_safe_path(project_dir)}\\#{check}" - shellout! "#{python} -m pip install datadog-#{check} --no-deps --no-index --find-links=#{wheel_build_dir}" - else - shellout! "#{pip} wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => nix_build_env, :cwd => "#{project_dir}/#{check}" - shellout! "#{pip} install datadog-#{check} --no-deps --no-index --find-links=#{wheel_build_dir}" - end - if cache_bucket != '' && ENV.fetch('INTEGRATION_WHEELS_SKIP_CACHE_UPLOAD', '') == '' && cache_branch != nil - shellout! "inv -e agent.upload-integration-to-cache " \ - "--python 2 --bucket #{cache_bucket} " \ - "--branch #{cache_branch} " \ - "--integrations-dir #{windows_safe_path(project_dir)} " \ - "--build-dir #{wheel_build_dir} " \ - "--integration #{check} " \ - "--awscli #{awscli}", - :cwd => tasks_dir_in - end - end - end - - # From now on we don't need piptools anymore, uninstall its deps so we don't include them in the final artifact - uninstall_buildtime_deps.each do |dep| - if windows_target? - command "#{python} -m pip uninstall -y #{dep}" - else - command "#{pip} uninstall -y #{dep}" - end - end - - # Patch applies to only one file: set it explicitly as a target, no need for -p - if windows_target? - patch :source => "create-regex-at-runtime.patch", :target => "#{python_2_embedded}/Lib/site-packages/yaml/reader.py" - patch :source => "remove-maxfile-maxpath-psutil.patch", :target => "#{python_2_embedded}/Lib/site-packages/psutil/__init__.py" - else - patch :source => "create-regex-at-runtime.patch", :target => "#{install_dir}/embedded/lib/python2.7/site-packages/yaml/reader.py" - patch :source => "remove-maxfile-maxpath-psutil.patch", :target => "#{install_dir}/embedded/lib/python2.7/site-packages/psutil/__init__.py" - end - - # Run pip check to make sure the agent's python environment is clean, all the dependencies are compatible - if windows_target? - command "#{python} -m pip check" - else - command "#{pip} check" - end - - # Removing tests that don't need to be shipped in the embedded folder - if windows_target? - delete "#{python_2_embedded}/Lib/site-packages/Cryptodome/SelfTest/" - else - delete "#{install_dir}/embedded/lib/python2.7/site-packages/Cryptodome/SelfTest/" - end - - # Ship `requirements-agent-release.txt` file containing the versions of every check shipped with the agent - # Used by the `datadog-agent integration` command to prevent downgrading a check to a version - # older than the one shipped in the agent - copy "#{project_dir}/requirements-agent-release.txt", "#{install_dir}/" -end diff --git a/omnibus/config/software/datadog-agent-integrations-py3.rb b/omnibus/config/software/datadog-agent-integrations-py3.rb deleted file mode 100644 index ffb437079370d..0000000000000 --- a/omnibus/config/software/datadog-agent-integrations-py3.rb +++ /dev/null @@ -1,490 +0,0 @@ -# Unless explicitly stated otherwise all files in this repository are licensed -# under the Apache License Version 2.0. -# This product includes software developed at Datadog (https:#www.datadoghq.com/). -# Copyright 2016-present Datadog, Inc. - -require './lib/ostools.rb' -require 'json' - -name 'datadog-agent-integrations-py3' - -license "BSD-3-Clause" -license_file "./LICENSE" - -dependency 'datadog-agent' -dependency 'datadog-agent-integrations-py3-dependencies' - -relative_path 'integrations-core' -whitelist_file "embedded/lib/python3.11/site-packages/.libsaerospike" -whitelist_file "embedded/lib/python3.11/site-packages/aerospike.libs" -whitelist_file "embedded/lib/python3.11/site-packages/psycopg2" -whitelist_file "embedded/lib/python3.11/site-packages/pymqi" - -source git: 'https://github.com/DataDog/integrations-core.git' - -gcc_version = ENV['GCC_VERSION'] -if gcc_version.nil? || gcc_version.empty? - gcc_version = '10.4.0' -end - -integrations_core_version = ENV['INTEGRATIONS_CORE_VERSION'] -if integrations_core_version.nil? || integrations_core_version.empty? - integrations_core_version = 'master' -end -default_version integrations_core_version - -# folder names containing integrations from -core that won't be packaged with the Agent -excluded_folders = [ - 'datadog_checks_base', # namespacing package for wheels (NOT AN INTEGRATION) - 'datadog_checks_dev', # Development package, (NOT AN INTEGRATION) - 'datadog_checks_tests_helper', # Testing and Development package, (NOT AN INTEGRATION) - 'docker_daemon', # Agent v5 only -] - -# package names of dependencies that won't be added to the Agent Python environment -excluded_packages = Array.new - -# We build these manually -excluded_packages.push(/^snowflake-connector-python==/) -excluded_packages.push(/^confluent-kafka==/) - -if suse_target? - # Temporarily exclude Aerospike until builder supports new dependency - excluded_packages.push(/^aerospike==/) - excluded_folders.push('aerospike') -end - -if osx_target? - # Temporarily exclude Aerospike until builder supports new dependency - excluded_packages.push(/^aerospike==/) - excluded_folders.push('aerospike') - excluded_folders.push('teradata') -end - -if arm_target? - # This doesn't build on ARM - excluded_folders.push('ibm_ace') - excluded_folders.push('ibm_mq') - excluded_packages.push(/^pymqi==/) -end - -# We explicitly check for redhat builder, not target -# Our centos/redhat builder uses glibc 2.12 while pydantic -# requires glibc 2.17 -if redhat? && !arm_target? - excluded_packages.push(/^pydantic-core==/) -end - -# _64_bit checks the kernel arch. On windows, the builder is 64 bit -# even when doing a 32 bit build. Do a specific check for the 32 bit -# build -if arm_target? || !_64_bit? || (windows_target? && windows_arch_i386?) - excluded_packages.push(/^orjson==/) -end - -if linux_target? - excluded_packages.push(/^oracledb==/) -end - -final_constraints_file = 'final_constraints-py3.txt' -agent_requirements_file = 'agent_requirements-py3.txt' -filtered_agent_requirements_in = 'agent_requirements-py3.in' -agent_requirements_in = 'agent_requirements.in' - -build do - # The dir for confs - if osx_target? - conf_dir = "#{install_dir}/etc/conf.d" - else - conf_dir = "#{install_dir}/etc/datadog-agent/conf.d" - end - mkdir conf_dir - - # aliases for pip - if windows_target? - python = "#{windows_safe_path(python_3_embedded)}\\python.exe" - else - python = "#{install_dir}/embedded/bin/python3" - end - - # If a python_mirror is set, it is set in a pip config file so that we do not leak the token in the CI output - pip_config_file = ENV['PIP_CONFIG_FILE'] - pre_build_env = { - "PIP_CONFIG_FILE" => "#{pip_config_file}" - } - - # Install the checks along with their dependencies - if windows_target? - wheel_build_dir = "#{windows_safe_path(project_dir)}\\.wheels" - build_deps_dir = "#{windows_safe_path(project_dir)}\\.build_deps" - else - wheel_build_dir = "#{project_dir}/.wheels" - build_deps_dir = "#{project_dir}/.build_deps" - end - - # - # Prepare the build env, these dependencies are only needed to build and - # install the core integrations. - # - command "#{python} -m pip download --dest #{build_deps_dir} hatchling==0.25.1", :env => pre_build_env - command "#{python} -m pip download --dest #{build_deps_dir} setuptools==66.1.1", :env => pre_build_env # Version from ./setuptools3.rb - command "#{python} -m pip install wheel==0.38.4", :env => pre_build_env - command "#{python} -m pip install pip-tools==7.3.0", :env => pre_build_env - uninstall_buildtime_deps = ['rtloader', 'click', 'first', 'pip-tools'] - nix_build_env = { - "PIP_FIND_LINKS" => "#{build_deps_dir}", - "PIP_CONFIG_FILE" => "#{pip_config_file}", - # Specify C99 standard explicitly to avoid issues while building some - # wheels (eg. ddtrace) - "CFLAGS" => "-I#{install_dir}/embedded/include -I/opt/mqm/inc", - "CXXFLAGS" => "-I#{install_dir}/embedded/include -I/opt/mqm/inc", - "LDFLAGS" => "-L#{install_dir}/embedded/lib -L/opt/mqm/lib64 -L/opt/mqm/lib", - "LD_RUN_PATH" => "#{install_dir}/embedded/lib -L/opt/mqm/lib64 -L/opt/mqm/lib", - "PATH" => "#{install_dir}/embedded/bin:#{ENV['PATH']}", - } - - win_build_env = { - "PIP_FIND_LINKS" => "#{build_deps_dir}", - "PIP_CONFIG_FILE" => "#{pip_config_file}", - } - - # Some libraries (looking at you, aerospike-client-python) need EXT_CFLAGS instead of CFLAGS. - nix_specific_build_env = { - "aerospike" => nix_build_env.merge({"EXT_CFLAGS" => nix_build_env["CFLAGS"] + " -std=gnu99"}), - # Always build pyodbc from source to link to the embedded version of libodbc - "pyodbc" => nix_build_env.merge({"PIP_NO_BINARY" => "pyodbc"}), - } - win_specific_build_env = {} - - # On Linux & Windows, specify the C99 standard explicitly to avoid issues while building some - # wheels (eg. ddtrace). - # Not explicitly setting that option has caused us problems in the past on SUSE, where the ddtrace - # wheel has to be manually built, as the C code in ddtrace doesn't follow the C89 standard (the default value of std). - # Note: We don't set this on MacOS, as on MacOS we need to build a bunch of packages & C extensions that - # don't have precompiled MacOS wheels. When building C extensions, the CFLAGS variable is added to - # the command-line parameters, even when compiling C++ code, where -std=c99 is invalid. - # See: https://github.com/python/cpython/blob/v3.8.8/Lib/distutils/sysconfig.py#L227 - if linux_target? || windows_target? - nix_build_env["CFLAGS"] += " -std=c99" - end - - # We only have gcc 10.4.0 on linux for now - if linux_target? - nix_build_env["CC"] = "/opt/gcc-#{gcc_version}/bin/gcc" - nix_build_env["CXX"] = "/opt/gcc-#{gcc_version}/bin/g++" - end - - # We need to explicitly specify RUSTFLAGS for libssl and libcrypto - # See https://github.com/pyca/cryptography/issues/8614#issuecomment-1489366475 - if redhat_target? && !arm_target? - nix_specific_build_env["cryptography"] = nix_build_env.merge( - { - "RUSTFLAGS" => "-C link-arg=-Wl,-rpath,#{install_dir}/embedded/lib", - "OPENSSL_DIR" => "#{install_dir}/embedded/", - # We have a manually installed dependency (snowflake connector) that already installed cryptography (but without the flags) - # We force reinstall it from source to be sure we use the flag - "PIP_NO_CACHE_DIR" => "off", - "PIP_FORCE_REINSTALL" => "1", - } - ) - end - - # - # Prepare the requirements file containing ALL the dependencies needed by - # any integration. This will provide the "static Python environment" of the Agent. - # We don't use the .in file provided by the base check directly because we - # want to filter out things before installing. - # - if windows_target? - static_reqs_in_file = "#{windows_safe_path(project_dir)}\\datadog_checks_base\\datadog_checks\\base\\data\\#{agent_requirements_in}" - static_reqs_out_folder = "#{windows_safe_path(project_dir)}\\" - static_reqs_out_file = static_reqs_out_folder + filtered_agent_requirements_in - compiled_reqs_file_path = "#{windows_safe_path(install_dir)}\\#{agent_requirements_file}" - else - static_reqs_in_file = "#{project_dir}/datadog_checks_base/datadog_checks/base/data/#{agent_requirements_in}" - static_reqs_out_folder = "#{project_dir}/" - static_reqs_out_file = static_reqs_out_folder + filtered_agent_requirements_in - compiled_reqs_file_path = "#{install_dir}/#{agent_requirements_file}" - end - - specific_build_env = windows_target? ? win_specific_build_env : nix_specific_build_env - build_env = windows_target? ? win_build_env : nix_build_env - cwd_base = windows_target? ? "#{windows_safe_path(project_dir)}\\datadog_checks_base" : "#{project_dir}/datadog_checks_base" - cwd_downloader = windows_target? ? "#{windows_safe_path(project_dir)}\\datadog_checks_downloader" : "#{project_dir}/datadog_checks_downloader" - - # Creating a hash containing the requirements and requirements file path associated to every lib - requirements_custom = Hash.new() - specific_build_env.each do |lib, env| - lib_compiled_req_file_path = (windows_target? ? "#{windows_safe_path(install_dir)}\\" : "#{install_dir}/") + "agent_#{lib}_requirements-py3.txt" - requirements_custom[lib] = { - "req_lines" => Array.new, - "req_file_path" => static_reqs_out_folder + lib + "-py3.in", - "compiled_req_file_path" => lib_compiled_req_file_path, - } - end - - # Remove any excluded requirements from the static-environment req file - requirements = Array.new - - block "Create filtered requirements" do - File.open("#{static_reqs_in_file}", 'r+').readlines().each do |line| - next if excluded_packages.any? { |package_regex| line.match(package_regex) } - - # on non windows OS, we use the c version of the psycopg installation - if line.start_with?('psycopg[binary]') && !windows_target? - line.sub! 'psycopg[binary]', 'psycopg[c]' - end - # Keeping the custom env requirements lines apart to install them with a specific env - requirements_custom.each do |lib, lib_req| - if Regexp.new('^' + lib + '==').freeze.match line - lib_req["req_lines"].push(line) - end - end - # In any case we add the lib to the requirements files to avoid inconsistency in the installed versions - # For example if aerospike has dependency A>1.2.3 and a package in the big requirements file has A<1.2.3, the install process would succeed but the integration wouldn't work. - requirements.push(line) - end - - # Adding pympler for memory debug purposes - requirements.push("pympler==0.7") - end - - # Render the filtered requirements file - erb source: "static_requirements.txt.erb", - dest: "#{static_reqs_out_file}", - mode: 0640, - vars: { requirements: requirements } - - # Render the filtered libraries that are to be built with different env var - requirements_custom.each do |lib, lib_req| - erb source: "static_requirements.txt.erb", - dest: "#{lib_req["req_file_path"]}", - mode: 0640, - vars: { requirements: lib_req["req_lines"] } - end - - # Constraints file for constraining transitive dependencies in those cases where there may be incompatible versions - constraints = [] - if redhat_target? - constraints.push("bcrypt < 4.1.0") - end - - constraints_file = windows_safe_path(project_dir, "constraints.txt") - block "Write constraints file" do - File.open(constraints_file, 'w') { |f| f << constraints.join("\n") } - end - - - # Increasing pip max retries (default: 5 times) and pip timeout (default 15 seconds) to avoid blocking network errors - pip_max_retries = 20 - pip_timeout = 20 - - # Use pip-compile to create the final requirements file. Notice when we invoke `pip` through `python -m pip <...>`, - # there's no need to refer to `pip`, the interpreter will pick the right script. - command "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => build_env, :cwd => cwd_base - command "#{python} -m pip install datadog_checks_base --no-deps --no-index --find-links=#{wheel_build_dir}" - command "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => build_env, :cwd => cwd_downloader - command "#{python} -m pip install datadog_checks_downloader --no-deps --no-index --find-links=#{wheel_build_dir}" - command "#{python} -m piptools compile --generate-hashes -c #{constraints_file} --output-file #{compiled_reqs_file_path} #{static_reqs_out_file} " \ - "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => build_env - # Pip-compiling seperately each lib that needs a custom build installation - specific_build_env.each do |lib, env| - command "#{python} -m piptools compile --generate-hashes -c #{constraints_file} --output-file #{requirements_custom[lib]["compiled_req_file_path"]} #{requirements_custom[lib]["req_file_path"]} " \ - "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => env - end - - # - # Install static-environment requirements that the Agent and all checks will use - # - - # First we install the dependencies that need specific flags - specific_build_env.each do |lib, env| - command "#{python} -m pip install --no-deps --require-hashes -r #{requirements_custom[lib]["compiled_req_file_path"]}", :env => env - # Remove the file after use so it is not shipped - delete "#{requirements_custom[lib]["compiled_req_file_path"]}" - end - # Then we install the rest (already installed libraries will be ignored) with the main flags - command "#{python} -m pip install --no-deps --require-hashes -r #{compiled_reqs_file_path}", :env => build_env - # Remove the file after use so it is not shipped - delete "#{compiled_reqs_file_path}" - - # - # Install Core integrations - # - - # Create a constraint file after installing all the core dependencies and before any integration - # This is then used as a constraint file by the integration command to avoid messing with the agent's python environment - command "#{python} -m pip freeze > #{install_dir}/#{final_constraints_file}" - - if windows_target? - cached_wheels_dir = "#{windows_safe_path(wheel_build_dir)}\\.cached" - else - cached_wheels_dir = "#{wheel_build_dir}/.cached" - end - - checks_to_install = Array.new - - block "Collect integrations to install" do - # Go through every integration package in `integrations-core`, build and install - Dir.glob("#{project_dir}/*").each do |check_dir| - check = check_dir.split('/').last - - # do not install excluded integrations - next if !File.directory?("#{check_dir}") || excluded_folders.include?(check) - - # If there is no manifest file, then we should assume the folder does not - # contain a working check and move onto the next - manifest_file_path = "#{check_dir}/manifest.json" - - # If there is no manifest file, then we should assume the folder does not - # contain a working check and move onto the next - File.exist?(manifest_file_path) || next - - manifest = JSON.parse(File.read(manifest_file_path)) - if manifest.key?("supported_os") - manifest["supported_os"].include?(os) || next - else - if os == "mac_os" - tag = "Supported OS::macOS" - else - tag = "Supported OS::#{os.capitalize}" - end - - manifest["tile"]["classifier_tags"].include?(tag) || next - end - - File.file?("#{check_dir}/setup.py") || File.file?("#{check_dir}/pyproject.toml") || next - # Check if it supports Python 3. - support = `inv agent.check-supports-python-version #{check_dir} 3` - if support == "False" - log.info(log_key) { "Skipping '#{check}' since it does not support Python 3." } - next - end - - checks_to_install.push(check) - end - end - - installed_list = Array.new - cache_bucket = ENV.fetch('INTEGRATION_WHEELS_CACHE_BUCKET', '') - block "Install integrations" do - tasks_dir_in = windows_safe_path(Dir.pwd) - cache_branch = (shellout! "inv release.get-release-json-value base_branch", cwd: File.expand_path('..', tasks_dir_in)).stdout.strip - # On windows, `aws` actually executes Ruby's AWS SDK, but we want the Python one - awscli = if windows_target? then '"c:\Program files\python39\scripts\aws"' else 'aws' end - if cache_bucket != '' - mkdir cached_wheels_dir - shellout! "inv -e agent.get-integrations-from-cache " \ - "--python 3 --bucket #{cache_bucket} " \ - "--branch #{cache_branch || 'main'} " \ - "--integrations-dir #{windows_safe_path(project_dir)} " \ - "--target-dir #{cached_wheels_dir} " \ - "--integrations #{checks_to_install.join(',')} " \ - "--awscli #{awscli}", - :cwd => tasks_dir_in - - # install all wheels from cache in one pip invocation to speed things up - if windows_target? - shellout! "#{python} -m pip install --no-deps --no-index " \ - " --find-links #{windows_safe_path(cached_wheels_dir)} -r #{windows_safe_path(cached_wheels_dir)}\\found.txt" - else - shellout! "#{python} -m pip install --no-deps --no-index " \ - "--find-links #{cached_wheels_dir} -r #{cached_wheels_dir}/found.txt" - end - end - - # get list of integration wheels already installed from cache - if cache_bucket != '' - installed_out = (shellout! "#{python} -m pip list --format json").stdout - if $?.exitstatus == 0 - installed = JSON.parse(installed_out) - installed.each do |package| - package.each do |key, value| - if key == "name" && value.start_with?("datadog-") - installed_list.push(value["datadog-".length..-1]) - end - end - end - else - raise "Failed to list pip installed packages" - end - end - - checks_to_install.each do |check| - check_dir = File.join(project_dir, check) - check_conf_dir = "#{conf_dir}/#{check}.d" - - # For each conf file, if it already exists, that means the `datadog-agent` software def - # wrote it first. In that case, since the agent's confs take precedence, skip the conf - conf_files = ["conf.yaml.example", "conf.yaml.default", "metrics.yaml", "auto_conf.yaml"] - conf_files.each do |filename| - src = windows_safe_path(check_dir,"datadog_checks", check, "data", filename) - dest = check_conf_dir - if File.exist?(src) and !File.exist?(windows_safe_path(dest, filename)) - FileUtils.mkdir_p(dest) - FileUtils.cp_r(src, dest) - end - end - - # Copy SNMP profiles - profile_folders = ['profiles', 'default_profiles'] - profile_folders.each do |profile_folder| - folder_path = "#{check_dir}/datadog_checks/#{check}/data/#{profile_folder}" - if File.exist? folder_path - FileUtils.cp_r folder_path, "#{check_conf_dir}/" - end - end - - # pip < 21.2 replace underscores by dashes in package names per https://pip.pypa.io/en/stable/news/#v21-2 - # whether or not this might switch back in the future is not guaranteed, so we check for both name - # with dashes and underscores - if installed_list.include?(check) || installed_list.include?(check.gsub('_', '-')) - next - end - - if windows_target? - shellout! "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => win_build_env, :cwd => "#{windows_safe_path(project_dir)}\\#{check}" - else - shellout! "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => nix_build_env, :cwd => "#{project_dir}/#{check}" - end - shellout! "#{python} -m pip install datadog-#{check} --no-deps --no-index --find-links=#{wheel_build_dir}" - if cache_bucket != '' && ENV.fetch('INTEGRATION_WHEELS_SKIP_CACHE_UPLOAD', '') == '' && cache_branch != nil - shellout! "inv -e agent.upload-integration-to-cache " \ - "--python 3 --bucket #{cache_bucket} " \ - "--branch #{cache_branch} " \ - "--integrations-dir #{windows_safe_path(project_dir)} " \ - "--build-dir #{wheel_build_dir} " \ - "--integration #{check} " \ - "--awscli #{awscli}", - :cwd => tasks_dir_in - end - end - end - - # From now on we don't need piptools anymore, uninstall its deps so we don't include them in the final artifact - uninstall_buildtime_deps.each do |dep| - command "#{python} -m pip uninstall -y #{dep}" - end - - # Patch applies to only one file: set it explicitly as a target, no need for -p - if windows_target? - patch :source => "remove-maxfile-maxpath-psutil.patch", :target => "#{python_3_embedded}/Lib/site-packages/psutil/__init__.py" - else - patch :source => "remove-maxfile-maxpath-psutil.patch", :target => "#{install_dir}/embedded/lib/python3.11/site-packages/psutil/__init__.py" - end - - # Run pip check to make sure the agent's python environment is clean, all the dependencies are compatible - command "#{python} -m pip check" - - # Removing tests that don't need to be shipped in the embedded folder - if windows_target? - delete "#{python_3_embedded}/Lib/site-packages/Cryptodome/SelfTest/" - else - delete "#{install_dir}/embedded/lib/python3.11/site-packages/Cryptodome/SelfTest/" - end - - # Ship `requirements-agent-release.txt` file containing the versions of every check shipped with the agent - # Used by the `datadog-agent integration` command to prevent downgrading a check to a version - # older than the one shipped in the agent - copy "#{project_dir}/requirements-agent-release.txt", "#{install_dir}/" -end diff --git a/omnibus/config/software/datadog-agent-integrations.rb b/omnibus/config/software/datadog-agent-integrations.rb new file mode 100644 index 0000000000000..fa21fe029973a --- /dev/null +++ b/omnibus/config/software/datadog-agent-integrations.rb @@ -0,0 +1,524 @@ +# Unless explicitly stated otherwise all files in this repository are licensed +# under the Apache License Version 2.0. +# This product includes software developed at Datadog (https:#www.datadoghq.com/). +# Copyright 2016-present Datadog, Inc. + +require './lib/ostools.rb' +require 'json' + +name 'datadog-agent-integrations' + +license "BSD-3-Clause" +license_file "./LICENSE" + +source git: 'https://github.com/DataDog/integrations-core.git' +relative_path 'integrations-core' + +integrations_core_version = ENV['INTEGRATIONS_CORE_VERSION'] +if integrations_core_version.nil? || integrations_core_version.empty? + integrations_core_version = 'master' +end + +default_version integrations_core_version + +dependency 'datadog-agent' + +if with_python_runtime? "2" + dependency 'datadog-agent-integrations-py2-dependencies' + + whitelist_file "embedded/lib/python2.7/site-packages/.libsaerospike" + whitelist_file "embedded/lib/python2.7/site-packages/psycopg2" + whitelist_file "embedded/lib/python2.7/site-packages/wrapt" + whitelist_file "embedded/lib/python2.7/site-packages/pymqi" +end + +if with_python_runtime? "3" + dependency 'datadog-agent-integrations-py3-dependencies' + + whitelist_file "embedded/lib/python3.11/site-packages/.libsaerospike" + whitelist_file "embedded/lib/python3.11/site-packages/aerospike.libs" + whitelist_file "embedded/lib/python3.11/site-packages/psycopg2" + whitelist_file "embedded/lib/python3.11/site-packages/pymqi" +end + +gcc_version = ENV['GCC_VERSION'] +if gcc_version.nil? || gcc_version.empty? + gcc_version = '10.4.0' +end + +# folder names containing integrations from -core that won't be packaged with the Agent +excluded_folders = [ + 'datadog_checks_base', # namespacing package for wheels (NOT AN INTEGRATION) + 'datadog_checks_dev', # Development package, (NOT AN INTEGRATION) + 'datadog_checks_tests_helper', # Testing and Development package, (NOT AN INTEGRATION) + 'docker_daemon', # Agent v5 only +] + +# package names of dependencies that won't be added to the Agent Python environment +excluded_packages = Array.new + +# We build these manually +excluded_packages.push(/^snowflake-connector-python==/) +excluded_packages.push(/^confluent-kafka==/) + +if suse_target? + # Temporarily exclude Aerospike until builder supports new dependency + excluded_packages.push(/^aerospike==/) + excluded_folders.push('aerospike') +end + +if osx_target? + # Temporarily exclude Aerospike until builder supports new dependency + excluded_packages.push(/^aerospike==/) + excluded_folders.push('aerospike') + excluded_folders.push('teradata') +end + +if arm_target? + # This doesn't build on ARM + excluded_folders.push('ibm_ace') + excluded_folders.push('ibm_mq') + excluded_packages.push(/^pymqi==/) +end + +# We explicitly check for redhat builder, not target +# Our centos/redhat builder uses glibc 2.12 while pydantic +# requires glibc 2.17 +if redhat? && !arm_target? + excluded_packages.push(/^pydantic-core==/) +end + +# _64_bit checks the kernel arch. On windows, the builder is 64 bit +# even when doing a 32 bit build. Do a specific check for the 32 bit +# build +if arm_target? || !_64_bit? || (windows_target? && windows_arch_i386?) + excluded_packages.push(/^orjson==/) +end + +if linux_target? + excluded_packages.push(/^oracledb==/) +end + +build do + ["2", "3"].each do |python_major_version| + # Skip python version if not included in this build + next if not with_python_runtime?(python_major_version) + + final_constraints_file = "final_constraints-py#{python_major_version}.txt" + agent_requirements_file = "agent_requirements-py#{python_major_version}.txt" + filtered_agent_requirements_in = "agent_requirements-py#{python_major_version}.in" + agent_requirements_in = 'agent_requirements.in' + + # The dir for confs + if osx_target? + conf_dir = "#{install_dir}/etc/conf.d" + else + conf_dir = "#{install_dir}/etc/datadog-agent/conf.d" + end + mkdir conf_dir + + # aliases for pip + if windows_target? + python = windows_safe_path(install_dir, "embedded#{python_major_version}", "python.exe") + else + python = "#{install_dir}/embedded/bin/python#{python_major_version}" + end + + # If a python_mirror is set, it is set in a pip config file so that we do not leak the token in the CI output + pip_config_file = ENV['PIP_CONFIG_FILE'] + pre_build_env = { + "PIP_CONFIG_FILE" => "#{pip_config_file}" + } + + # Install the checks along with their dependencies + wheel_build_dir = windows_safe_path(project_dir, python_major_version, ".wheels") + build_deps_dir = windows_safe_path(project_dir, python_major_version, ".build_deps") + + # + # Prepare the build env, these dependencies are only needed to build and + # install the core integrations. + # + if python_major_version == "2" + command "#{python} -m pip download --dest #{build_deps_dir} hatchling==0.25.1", :env => pre_build_env + command "#{python} -m pip download --dest #{build_deps_dir} setuptools==40.9.0", :env => pre_build_env # Version from ./setuptools2.rb + command "#{python} -m pip install wheel==0.37.1", :env => pre_build_env # Pin to the last version that supports Python 2 + command "#{python} -m pip install setuptools-scm==5.0.2", :env => pre_build_env # Pin to the last version that supports Python 2 + command "#{python} -m pip install pip-tools==5.4.0", :env => pre_build_env + else + command "#{python} -m pip download --dest #{build_deps_dir} hatchling==0.25.1", :env => pre_build_env + command "#{python} -m pip download --dest #{build_deps_dir} setuptools==66.1.1", :env => pre_build_env # Version from ./setuptools3.rb + command "#{python} -m pip install wheel==0.38.4", :env => pre_build_env + command "#{python} -m pip install pip-tools==7.3.0", :env => pre_build_env + end + uninstall_buildtime_deps = ['rtloader', 'click', 'first', 'pip-tools'] + nix_build_env = { + "PIP_FIND_LINKS" => "#{build_deps_dir}", + "PIP_CONFIG_FILE" => "#{pip_config_file}", + "CFLAGS" => "-I#{install_dir}/embedded/include -I/opt/mqm/inc", + "CXXFLAGS" => "-I#{install_dir}/embedded/include -I/opt/mqm/inc", + "LDFLAGS" => "-L#{install_dir}/embedded/lib -L/opt/mqm/lib64 -L/opt/mqm/lib", + "LD_RUN_PATH" => "#{install_dir}/embedded/lib -L/opt/mqm/lib64 -L/opt/mqm/lib", + "PATH" => "#{install_dir}/embedded/bin:#{ENV['PATH']}", + } + + win_build_env = { + "PIP_FIND_LINKS" => "#{build_deps_dir}", + "PIP_CONFIG_FILE" => "#{pip_config_file}", + } + + # Some libraries (looking at you, aerospike-client-python) need EXT_CFLAGS instead of CFLAGS. + nix_specific_build_env = { + "aerospike" => nix_build_env.merge({"EXT_CFLAGS" => nix_build_env["CFLAGS"] + " -std=gnu99"}), + # Always build pyodbc from source to link to the embedded version of libodbc + "pyodbc" => nix_build_env.merge({"PIP_NO_BINARY" => "pyodbc"}), + } + win_specific_build_env = {} + + # On Linux & Windows, specify the C99 standard explicitly to avoid issues while building some + # wheels (eg. ddtrace). + # Not explicitly setting that option has caused us problems in the past on SUSE, where the ddtrace + # wheel has to be manually built, as the C code in ddtrace doesn't follow the C89 standard (the default value of std). + # Note: We don't set this on MacOS, as on MacOS we need to build a bunch of packages & C extensions that + # don't have precompiled MacOS wheels. When building C extensions, the CFLAGS variable is added to + # the command-line parameters, even when compiling C++ code, where -std=c99 is invalid. + # See: https://github.com/python/cpython/blob/v3.8.8/Lib/distutils/sysconfig.py#L227 + if linux_target? || windows_target? + nix_build_env["CFLAGS"] += " -std=c99" + end + + # We only have gcc 10.4.0 on linux for now + if linux_target? + nix_build_env["CC"] = "/opt/gcc-#{gcc_version}/bin/gcc" + nix_build_env["CXX"] = "/opt/gcc-#{gcc_version}/bin/g++" + end + + # We need to explicitly specify RUSTFLAGS for libssl and libcrypto + # See https://github.com/pyca/cryptography/issues/8614#issuecomment-1489366475 + if python_major_version == "3" && redhat_target? && !arm_target? + nix_specific_build_env["cryptography"] = nix_build_env.merge( + { + "RUSTFLAGS" => "-C link-arg=-Wl,-rpath,#{install_dir}/embedded/lib", + "OPENSSL_DIR" => "#{install_dir}/embedded/", + # We have a manually installed dependency (snowflake connector) that already installed cryptography (but without the flags) + # We force reinstall it from source to be sure we use the flag + "PIP_NO_CACHE_DIR" => "off", + "PIP_FORCE_REINSTALL" => "1", + } + ) + end + + # + # Prepare the requirements file containing ALL the dependencies needed by + # any integration. This will provide the "static Python environment" of the Agent. + # We don't use the .in file provided by the base check directly because we + # want to filter out things before installing. + # + if windows_target? + static_reqs_in_file = "#{windows_safe_path(project_dir)}\\datadog_checks_base\\datadog_checks\\base\\data\\#{agent_requirements_in}" + static_reqs_out_folder = "#{windows_safe_path(project_dir)}\\" + static_reqs_out_file = static_reqs_out_folder + filtered_agent_requirements_in + compiled_reqs_file_path = "#{windows_safe_path(install_dir)}\\#{agent_requirements_file}" + else + static_reqs_in_file = "#{project_dir}/datadog_checks_base/datadog_checks/base/data/#{agent_requirements_in}" + static_reqs_out_folder = "#{project_dir}/" + static_reqs_out_file = static_reqs_out_folder + filtered_agent_requirements_in + compiled_reqs_file_path = "#{install_dir}/#{agent_requirements_file}" + end + + specific_build_env = windows_target? ? win_specific_build_env : nix_specific_build_env + build_env = windows_target? ? win_build_env : nix_build_env + cwd_base = windows_target? ? "#{windows_safe_path(project_dir)}\\datadog_checks_base" : "#{project_dir}/datadog_checks_base" + cwd_downloader = windows_target? ? "#{windows_safe_path(project_dir)}\\datadog_checks_downloader" : "#{project_dir}/datadog_checks_downloader" + + # Creating a hash containing the requirements and requirements file path associated to every lib + requirements_custom = Hash.new() + specific_build_env.each do |lib, env| + lib_compiled_req_file_path = (windows_target? ? "#{windows_safe_path(install_dir)}\\" : "#{install_dir}/") + "agent_#{lib}_requirements-py3.txt" + requirements_custom[lib] = { + "req_lines" => Array.new, + "req_file_path" => static_reqs_out_folder + lib + "-py#{python_major_version}.in", + "compiled_req_file_path" => lib_compiled_req_file_path, + } + end + + # Remove any excluded requirements from the static-environment req file + requirements = Array.new + + block "Create filtered requirements" do + File.open("#{static_reqs_in_file}", 'r+').readlines().each do |line| + next if excluded_packages.any? { |package_regex| line.match(package_regex) } + + # on non windows OS, we use the c version of the psycopg installation + if line.start_with?('psycopg[binary]') && !windows_target? + line.sub! 'psycopg[binary]', 'psycopg[c]' + end + # Keeping the custom env requirements lines apart to install them with a specific env + requirements_custom.each do |lib, lib_req| + if Regexp.new('^' + lib + '==').freeze.match line + lib_req["req_lines"].push(line) + end + end + # In any case we add the lib to the requirements files to avoid inconsistency in the installed versions + # For example if aerospike has dependency A>1.2.3 and a package in the big requirements file has A<1.2.3, the install process would succeed but the integration wouldn't work. + requirements.push(line) + end + + # Adding pympler for memory debug purposes + requirements.push("pympler==0.7") + + end + + # Render the filtered requirements file + erb source: "static_requirements.txt.erb", + dest: "#{static_reqs_out_file}", + mode: 0640, + vars: { requirements: requirements } + + # Render the filtered libraries that are to be built with different env var + requirements_custom.each do |lib, lib_req| + erb source: "static_requirements.txt.erb", + dest: "#{lib_req["req_file_path"]}", + mode: 0640, + vars: { requirements: lib_req["req_lines"] } + end + + # Constraints file for constraining transitive dependencies in those cases where there may be incompatible versions (only supported for py3) + constraints_flag = "" + if python_major_version == "3" + constraints = [] + if redhat_target? + constraints.push("bcrypt < 4.1.0") + end + + constraints_file = windows_safe_path(project_dir, "constraints.txt") + block "Write constraints file" do + File.open(constraints_file, 'w') { |f| f << constraints.join("\n") } + end + constraints_flag = "-c #{constraints_file}" + end + + # Increasing pip max retries (default: 5 times) and pip timeout (default 15 seconds) to avoid blocking network errors + pip_max_retries = 20 + pip_timeout = 20 + + # Use pip-compile to create the final requirements file. Notice when we invoke `pip` through `python -m pip <...>`, + # there's no need to refer to `pip`, the interpreter will pick the right script. + command "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => build_env, :cwd => cwd_base + command "#{python} -m pip install datadog_checks_base --no-deps --no-index --find-links=#{wheel_build_dir}" + + # We only install the downloader on Python 3 + if python_major_version == "3" + command "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => build_env, :cwd => cwd_downloader + command "#{python} -m pip install datadog_checks_downloader --no-deps --no-index --find-links=#{wheel_build_dir}" + end + + command "#{python} -m piptools compile --generate-hashes #{constraints_flag} --output-file #{compiled_reqs_file_path} #{static_reqs_out_file} " \ + "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => build_env + # Pip-compiling seperately each lib that needs a custom build installation + specific_build_env.each do |lib, env| + command "#{python} -m piptools compile --generate-hashes #{constraints_flag} --output-file #{requirements_custom[lib]["compiled_req_file_path"]} #{requirements_custom[lib]["req_file_path"]} " \ + "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => env + end + + # + # Install static-environment requirements that the Agent and all checks will use + # + + # First we install the dependencies that need specific flags + specific_build_env.each do |lib, env| + command "#{python} -m pip install --no-deps --require-hashes -r #{requirements_custom[lib]["compiled_req_file_path"]}", :env => env + # Remove the file after use so it is not shipped + delete "#{requirements_custom[lib]["compiled_req_file_path"]}" + end + # Then we install the rest (already installed libraries will be ignored) with the main flags + command "#{python} -m pip install --no-deps --require-hashes -r #{compiled_reqs_file_path}", :env => build_env + # Remove the file after use so it is not shipped + delete "#{compiled_reqs_file_path}" + + # + # Install Core integrations + # + + # Create a constraint file after installing all the core dependencies and before any integration + # This is then used as a constraint file by the integration command to avoid messing with the agent's python environment + command "#{python} -m pip freeze > #{install_dir}/#{final_constraints_file}" + + if windows_target? + cached_wheels_dir = "#{windows_safe_path(wheel_build_dir)}\\.cached" + else + cached_wheels_dir = "#{wheel_build_dir}/.cached" + end + + checks_to_install = Array.new + + block "Collect integrations to install" do + # Go through every integration package in `integrations-core`, build and install + Dir.glob("#{project_dir}/*").each do |check_dir| + check = check_dir.split('/').last + + # do not install excluded integrations + next if !File.directory?("#{check_dir}") || excluded_folders.include?(check) + + # If there is no manifest file, then we should assume the folder does not + # contain a working check and move onto the next + manifest_file_path = "#{check_dir}/manifest.json" + + # If there is no manifest file, then we should assume the folder does not + # contain a working check and move onto the next + File.exist?(manifest_file_path) || next + + manifest = JSON.parse(File.read(manifest_file_path)) + if manifest.key?("supported_os") + manifest["supported_os"].include?(os) || next + else + if os == "mac_os" + tag = "Supported OS::macOS" + else + tag = "Supported OS::#{os.capitalize}" + end + + manifest["tile"]["classifier_tags"].include?(tag) || next + end + + File.file?("#{check_dir}/setup.py") || File.file?("#{check_dir}/pyproject.toml") || next + # Check if it supports Python version we're building for. + support = `inv agent.check-supports-python-version #{check_dir} #{python_major_version}` + if support == "False" + log.info(log_key) { "Skipping '#{check}' since it does not support Python #{python_major_version}." } + next + end + + checks_to_install.push(check) + end + end + + installed_list = Array.new + cache_bucket = ENV.fetch('INTEGRATION_WHEELS_CACHE_BUCKET', '') + block "Install integrations" do + tasks_dir_in = windows_safe_path(Dir.pwd) + cache_branch = (shellout! "inv release.get-release-json-value base_branch", cwd: File.expand_path('..', tasks_dir_in)).stdout.strip + # On windows, `aws` actually executes Ruby's AWS SDK, but we want the Python one + awscli = if windows_target? then '"c:\Program files\python39\scripts\aws"' else 'aws' end + if cache_bucket != '' + mkdir cached_wheels_dir + shellout! "inv -e agent.get-integrations-from-cache " \ + "--python #{python_major_version} --bucket #{cache_bucket} " \ + "--branch #{cache_branch || 'main'} " \ + "--integrations-dir #{windows_safe_path(project_dir)} " \ + "--target-dir #{cached_wheels_dir} " \ + "--integrations #{checks_to_install.join(',')} " \ + "--awscli #{awscli}", + :cwd => tasks_dir_in + + # install all wheels from cache in one pip invocation to speed things up + if windows_target? + shellout! "#{python} -m pip install --no-deps --no-index " \ + " --find-links #{windows_safe_path(cached_wheels_dir)} -r #{windows_safe_path(cached_wheels_dir)}\\found.txt" + else + shellout! "#{python} -m pip install --no-deps --no-index " \ + "--find-links #{cached_wheels_dir} -r #{cached_wheels_dir}/found.txt" + end + end + + # get list of integration wheels already installed from cache + if cache_bucket != '' + installed_out = (shellout! "#{python} -m pip list --format json").stdout + if $?.exitstatus == 0 + installed = JSON.parse(installed_out) + installed.each do |package| + package.each do |key, value| + if key == "name" && value.start_with?("datadog-") + installed_list.push(value["datadog-".length..-1]) + end + end + end + else + raise "Failed to list pip installed packages" + end + end + + checks_to_install.each do |check| + check_dir = File.join(project_dir, check) + check_conf_dir = "#{conf_dir}/#{check}.d" + + # For each conf file, if it already exists, that means the `datadog-agent` software def + # wrote it first. In that case, since the agent's confs take precedence, skip the conf + conf_files = ["conf.yaml.example", "conf.yaml.default", "metrics.yaml", "auto_conf.yaml"] + conf_files.each do |filename| + src = windows_safe_path(check_dir,"datadog_checks", check, "data", filename) + dest = check_conf_dir + if File.exist?(src) and !File.exist?(windows_safe_path(dest, filename)) + FileUtils.mkdir_p(dest) + FileUtils.cp_r(src, dest) + end + end + + # Copy SNMP profiles + profile_folders = ['profiles', 'default_profiles'] + profile_folders.each do |profile_folder| + folder_path = "#{check_dir}/datadog_checks/#{check}/data/#{profile_folder}" + if File.exist? folder_path + FileUtils.cp_r folder_path, "#{check_conf_dir}/" + end + end + + # pip < 21.2 replace underscores by dashes in package names per https://pip.pypa.io/en/stable/news/#v21-2 + # whether or not this might switch back in the future is not guaranteed, so we check for both name + # with dashes and underscores + if installed_list.include?(check) || installed_list.include?(check.gsub('_', '-')) + next + end + + if windows_target? + shellout! "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => win_build_env, :cwd => "#{windows_safe_path(project_dir)}\\#{check}" + else + shellout! "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => nix_build_env, :cwd => "#{project_dir}/#{check}" + end + shellout! "#{python} -m pip install datadog-#{check} --no-deps --no-index --find-links=#{wheel_build_dir}" + if cache_bucket != '' && ENV.fetch('INTEGRATION_WHEELS_SKIP_CACHE_UPLOAD', '') == '' && cache_branch != nil + shellout! "inv -e agent.upload-integration-to-cache " \ + "--python #{python_major_version} --bucket #{cache_bucket} " \ + "--branch #{cache_branch} " \ + "--integrations-dir #{windows_safe_path(project_dir)} " \ + "--build-dir #{wheel_build_dir} " \ + "--integration #{check} " \ + "--awscli #{awscli}", + :cwd => tasks_dir_in + end + end + end + + # From now on we don't need piptools anymore, uninstall its deps so we don't include them in the final artifact + uninstall_buildtime_deps.each do |dep| + command "#{python} -m pip uninstall -y #{dep}" + end + + if windows_target? + site_packages = windows_safe_path(install_dir, "embedded#{python_major_version}", "Lib/site-packages") + else + if python_major_version == '2' + site_packages = File.join(install_dir, "embedded/lib/python2.7/site-packages") + else + site_packages = File.join(install_dir, "embedded/lib/python3.11/site-packages") + end + end + + # Patch applies to only one file: set it explicitly as a target, no need for -p + patch :source => "remove-maxfile-maxpath-psutil.patch", :target => windows_safe_path(site_packages, "psutil/__init__.py") + + if python_major_version == '2' + patch :source => "create-regex-at-runtime.patch", :target => windows_safe_path(site_packages, "yaml/reader.py") + end + + # Run pip check to make sure the agent's python environment is clean, all the dependencies are compatible + command "#{python} -m pip check" + + # Removing tests that don't need to be shipped in the embedded folder + delete windows_safe_path(site_packages, "Cryptodome/SelfTest") + + # Ship `requirements-agent-release.txt` file containing the versions of every check shipped with the agent + # Used by the `datadog-agent integration` command to prevent downgrading a check to a version + # older than the one shipped in the agent + copy "#{project_dir}/requirements-agent-release.txt", "#{install_dir}/" + end +end diff --git a/omnibus/config/templates/datadog-agent-integrations-py3/static_requirements.txt.erb b/omnibus/config/templates/datadog-agent-integrations-py3/static_requirements.txt.erb deleted file mode 100644 index cf827abae0167..0000000000000 --- a/omnibus/config/templates/datadog-agent-integrations-py3/static_requirements.txt.erb +++ /dev/null @@ -1,3 +0,0 @@ -<% requirements.each do |requirement| -%> -<%= requirement -%> -<% end -%> diff --git a/omnibus/config/templates/datadog-agent-integrations-py2/static_requirements.txt.erb b/omnibus/config/templates/datadog-agent-integrations/static_requirements.txt.erb similarity index 100% rename from omnibus/config/templates/datadog-agent-integrations-py2/static_requirements.txt.erb rename to omnibus/config/templates/datadog-agent-integrations/static_requirements.txt.erb From 11c4e166f9a9061ca1b26b3181020911b59e46d7 Mon Sep 17 00:00:00 2001 From: Pierre Gimalac Date: Wed, 29 Nov 2023 15:03:35 +0100 Subject: [PATCH 71/87] [ASCII-947] Add instrumentation to help debug secrets test timeout issue (#21159) * feat: print stack traces after 2 minutes if the test is not finished * add a sleep to test that the artifact can be retrieved * it works, removing sleep --- .../secrets/secretsimpl/fetch_secret_test.go | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/comp/core/secrets/secretsimpl/fetch_secret_test.go b/comp/core/secrets/secretsimpl/fetch_secret_test.go index 15f0ba5ae21bd..bbd23a699b495 100644 --- a/comp/core/secrets/secretsimpl/fetch_secret_test.go +++ b/comp/core/secrets/secretsimpl/fetch_secret_test.go @@ -11,11 +11,14 @@ import ( "os" "os/exec" "runtime" + "runtime/pprof" "testing" + "time" - "github.com/DataDog/datadog-agent/comp/core/secrets" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/DataDog/datadog-agent/comp/core/secrets" ) var ( @@ -81,6 +84,35 @@ func TestLimitBuffer(t *testing.T) { } func TestExecCommandError(t *testing.T) { + // TODO: remove once the issue is resolved + // + // This test has an issue on Windows where it can block entirely and timeout after 4 minutes + // (while the go test timeout is 3 minutes), and in that case it doesn't print stack traces. + // + // As a workaround, we explicitly write routine stack traces in an artifact if the test + // is not finished after 2 minutes. + if _, ok := os.LookupEnv("CI_PIPELINE_ID"); ok && runtime.GOOS == "windows" { + done := make(chan struct{}, 1) + defer func() { + done <- struct{}{} + }() + go func() { + select { + case <-done: + case <-time.After(2 * time.Minute): + // files junit-*.tgz are automatically considered as artifacts + file, err := os.OpenFile(`C:\mnt\junit-TestExecCommandError.tgz`, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + fmt.Fprintf(os.Stderr, "could not write stack traces: %v", err) + return + } + defer file.Close() + fmt.Fprintf(file, "test will timeout, printing goroutine stack traces:\n") + pprof.Lookup("goroutine").WriteTo(file, 2) + } + }() + } + inputPayload := "{\"version\": \"" + secrets.PayloadVersion + "\" , \"secrets\": [\"sec1\", \"sec2\"]}" t.Run("Empty secretBackendCommand", func(t *testing.T) { From 3f5d700b3a9e65e79b0e3ed09538bc42f5c45648 Mon Sep 17 00:00:00 2001 From: Olivier G <52180542+ogaca-dd@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:29:13 +0100 Subject: [PATCH 72/87] Fix URL in comp/README.md (#21171) --- comp/README.md | 122 ++++++++++++++++++++++---------------------- tasks/components.py | 2 +- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/comp/README.md b/comp/README.md index 2eb68e2495041..5ff6d34c8d7b7 100644 --- a/comp/README.md +++ b/comp/README.md @@ -4,165 +4,165 @@ This file lists all components defined in this repository, with their package summary. Click the links for more documentation. -## [comp/aggregator](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/aggregator) (Component Bundle) +## [comp/aggregator](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/aggregator) (Component Bundle) *Datadog Team*: agent-shared-components Package aggregator implements the "aggregator" bundle, -### [comp/aggregator/demultiplexer](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/aggregator/demultiplexer) +### [comp/aggregator/demultiplexer](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/aggregator/demultiplexer) Package demultiplexer defines the aggregator demultiplexer -### [comp/aggregator/diagnosesendermanager](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/aggregator/diagnosesendermanager) +### [comp/aggregator/diagnosesendermanager](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/aggregator/diagnosesendermanager) Package diagnosesendermanager defines the sender manager for the local diagnose check -## [comp/checks](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/checks) (Component Bundle) +## [comp/checks](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/checks) (Component Bundle) *Datadog Team*: agent-shared-components Package checks implements the "checks" bundle, for all of the component based agent checks -### [comp/checks/agentcrashdetect](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/checks/agentcrashdetect) +### [comp/checks/agentcrashdetect](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/checks/agentcrashdetect) *Datadog Team*: windows-kernel-integrations Package agentcrashdetect ... /* TODO: detailed doc comment for the component */ -### [comp/checks/winregistry](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/checks/winregistry) +### [comp/checks/winregistry](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/checks/winregistry) *Datadog Team*: windows-agent Package winregistry implements the Windows Registry check -## [comp/core](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/core) (Component Bundle) +## [comp/core](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/core) (Component Bundle) *Datadog Team*: agent-shared-components Package core implements the "core" bundle, providing services common to all agent flavors and binaries. -### [comp/core/config](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/core/config) +### [comp/core/config](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/core/config) Package config implements a component to handle agent configuration. This component temporarily wraps pkg/config. -### [comp/core/flare](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/core/flare) +### [comp/core/flare](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/core/flare) Package flare implements a component to generate flares from the agent. -### [comp/core/hostname](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/core/hostname) +### [comp/core/hostname](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/core/hostname) Package hostname exposes hostname.Get() as a component. -### [comp/core/log](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/core/log) +### [comp/core/log](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/core/log) Package log implements a component to handle logging internal to the agent. -### [comp/core/secrets](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/core/secrets) +### [comp/core/secrets](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/core/secrets) Package secrets decodes secret values by invoking the configured executable command -### [comp/core/sysprobeconfig](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/core/sysprobeconfig) +### [comp/core/sysprobeconfig](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/core/sysprobeconfig) *Datadog Team*: ebpf-platform Package sysprobeconfig implements a component to handle system-probe configuration. This component temporarily wraps pkg/config. -### [comp/core/telemetry](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/core/telemetry) +### [comp/core/telemetry](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/core/telemetry) Package telemetry implements a component for all agent telemetry. -### [comp/core/workloadmeta](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/core/workloadmeta) +### [comp/core/workloadmeta](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/core/workloadmeta) *Datadog Team*: container-integrations Package workloadmeta provides the workloadmeta component for the Datadog Agent -## [comp/dogstatsd](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/dogstatsd) (Component Bundle) +## [comp/dogstatsd](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/dogstatsd) (Component Bundle) *Datadog Team*: agent-metrics-logs -### [comp/dogstatsd/replay](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/dogstatsd/replay) +### [comp/dogstatsd/replay](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/dogstatsd/replay) Package server implements a component to run the dogstatsd capture/replay -### [comp/dogstatsd/server](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/dogstatsd/server) +### [comp/dogstatsd/server](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/dogstatsd/server) Package server implements a component to run the dogstatsd server -### [comp/dogstatsd/serverDebug](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/dogstatsd/serverDebug) +### [comp/dogstatsd/serverDebug](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/dogstatsd/serverDebug) Package serverdebug implements a component to run the dogstatsd server debug -### [comp/dogstatsd/statsd](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/dogstatsd/statsd) +### [comp/dogstatsd/statsd](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/dogstatsd/statsd) *Datadog Team*: agent-shared-components Package statsd implements a component to get a statsd client. -## [comp/forwarder](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/forwarder) (Component Bundle) +## [comp/forwarder](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/forwarder) (Component Bundle) *Datadog Team*: agent-shared-components Package forwarder implements the "forwarder" bundle -### [comp/forwarder/defaultforwarder](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/forwarder/defaultforwarder) +### [comp/forwarder/defaultforwarder](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/forwarder/defaultforwarder) Package defaultForwarder implements a component to send payloads to the backend -## [comp/languagedetection](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/languagedetection) (Component Bundle) +## [comp/languagedetection](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/languagedetection) (Component Bundle) *Datadog Team*: container-integrations Package languagedetection implements the "languagedetection" bundle -### [comp/languagedetection/client](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/languagedetection/client) +### [comp/languagedetection/client](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/languagedetection/client) Package client implements a component to send process metadata to the Cluster-Agent -## [comp/logs](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/logs) (Component Bundle) +## [comp/logs](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/logs) (Component Bundle) *Datadog Team*: agent-metrics-logs -### [comp/logs/agent](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/logs/agent) +### [comp/logs/agent](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/logs/agent) Package agent contains logs agent component. -## [comp/metadata](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/metadata) (Component Bundle) +## [comp/metadata](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/metadata) (Component Bundle) *Datadog Team*: agent-shared-components Package metadata implements the "metadata" bundle, providing services and support for all the metadata payload sent by the Agent. -### [comp/metadata/host](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/metadata/host) +### [comp/metadata/host](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/metadata/host) Package host implements a component to generate the 'host' metadata payload (also known as "v5"). -### [comp/metadata/inventoryagent](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/metadata/inventoryagent) +### [comp/metadata/inventoryagent](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/metadata/inventoryagent) Package inventoryagent implements a component to generate the 'datadog_agent' metadata payload for inventory. -### [comp/metadata/inventoryhost](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/metadata/inventoryhost) +### [comp/metadata/inventoryhost](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/metadata/inventoryhost) Package inventoryhost exposes the interface for the component to generate the 'host_metadata' metadata payload for inventory. -### [comp/metadata/resources](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/metadata/resources) +### [comp/metadata/resources](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/metadata/resources) Package resources implements a component to generate the 'resources' metadata payload. -### [comp/metadata/runner](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/metadata/runner) +### [comp/metadata/runner](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/metadata/runner) Package runner implements a component to generate metadata payload at the right interval. -## [comp/ndmtmp](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/ndmtmp) (Component Bundle) +## [comp/ndmtmp](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/ndmtmp) (Component Bundle) *Datadog Team*: network-device-monitoring @@ -170,133 +170,133 @@ Package ndmtmp implements the "ndmtmp" bundle, which exposes the default sender.Sender and the event platform forwarder. This is a temporary module intended for ndm internal use until these pieces are properly componentized. -### [comp/ndmtmp/forwarder](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/ndmtmp/forwarder) +### [comp/ndmtmp/forwarder](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/ndmtmp/forwarder) Package forwarder exposes the event platform forwarder for netflow. -## [comp/netflow](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/netflow) (Component Bundle) +## [comp/netflow](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/netflow) (Component Bundle) *Datadog Team*: network-device-monitoring Package netflow implements the "netflow" bundle, which listens for netflow packets, processes them, and forwards relevant data to the backend. -### [comp/netflow/config](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/netflow/config) +### [comp/netflow/config](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/netflow/config) Package config exposes the netflow configuration as a component. -### [comp/netflow/server](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/netflow/server) +### [comp/netflow/server](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/netflow/server) Package server implements a component that runs the netflow server. When running, it listens for network traffic according to configured listeners and aggregates traffic data to send to the backend. It does not expose any public methods. -## [comp/otelcol](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/otelcol) (Component Bundle) +## [comp/otelcol](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/otelcol) (Component Bundle) *Datadog Team*: opentelemetry Package otelcol contains the OTLP ingest bundle pipeline to be included into the agent components. -### [comp/otelcol/collector](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/otelcol/collector) +### [comp/otelcol/collector](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/otelcol/collector) Package collector implements the OpenTelemetry Collector component. -## [comp/process](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process) (Component Bundle) +## [comp/process](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process) (Component Bundle) *Datadog Team*: processes Package process implements the "process" bundle, providing components for the Process Agent -### [comp/process/apiserver](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/apiserver) +### [comp/process/apiserver](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/apiserver) Package apiserver initializes the api server that powers many subcommands. -### [comp/process/connectionscheck](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/connectionscheck) +### [comp/process/connectionscheck](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/connectionscheck) Package connectionscheck implements a component to handle Connections data collection in the Process Agent. -### [comp/process/containercheck](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/containercheck) +### [comp/process/containercheck](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/containercheck) Package containercheck implements a component to handle Container data collection in the Process Agent. -### [comp/process/expvars](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/expvars) +### [comp/process/expvars](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/expvars) Package expvars initializes the expvar server of the process agent. -### [comp/process/forwarders](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/forwarders) +### [comp/process/forwarders](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/forwarders) Package forwarders implements a component to provide forwarders used by the process agent. -### [comp/process/hostinfo](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/hostinfo) +### [comp/process/hostinfo](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/hostinfo) Package hostinfo wraps the hostinfo inside a component. This is useful because it is relied on by other components. -### [comp/process/podcheck](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/podcheck) +### [comp/process/podcheck](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/podcheck) Package podcheck implements a component to handle Kubernetes data collection in the Process Agent. -### [comp/process/processcheck](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/processcheck) +### [comp/process/processcheck](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/processcheck) Package processcheck implements a component to handle Process data collection in the Process Agent. -### [comp/process/processdiscoverycheck](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/processdiscoverycheck) +### [comp/process/processdiscoverycheck](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/processdiscoverycheck) Package processdiscoverycheck implements a component to handle Process Discovery data collection in the Process Agent for customers who do not pay for live processes. -### [comp/process/processeventscheck](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/processeventscheck) +### [comp/process/processeventscheck](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/processeventscheck) Package processeventscheck implements a component to handle Process Events data collection in the Process Agent. -### [comp/process/profiler](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/profiler) +### [comp/process/profiler](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/profiler) Package profiler implements a component to handle starting and stopping the internal profiler. -### [comp/process/rtcontainercheck](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/rtcontainercheck) +### [comp/process/rtcontainercheck](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/rtcontainercheck) Package rtcontainercheck implements a component to handle realtime Container data collection in the Process Agent. -### [comp/process/runner](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/runner) +### [comp/process/runner](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/runner) Package runner implements a component to run data collection checks in the Process Agent. -### [comp/process/submitter](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/process/submitter) +### [comp/process/submitter](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process/submitter) Package submitter implements a component to submit collected data in the Process Agent to supported Datadog intakes. -## [comp/remote-config](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/remote-config) (Component Bundle) +## [comp/remote-config](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/remote-config) (Component Bundle) *Datadog Team*: remote-config -### [comp/remote-config/rcclient](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/remote-config/rcclient) +### [comp/remote-config/rcclient](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/remote-config/rcclient) -## [comp/systray](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/systray) (Component Bundle) +## [comp/systray](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/systray) (Component Bundle) *Datadog Team*: windows-agent Package systray implements the Datadog Agent Manager System Tray -### [comp/systray/systray](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/systray/systray) +### [comp/systray/systray](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/systray/systray) Package systray provides a component for the system tray application -## [comp/trace](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/trace) (Component Bundle) +## [comp/trace](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/trace) (Component Bundle) *Datadog Team*: agent-apm Package trace implements the "trace" bundle, providing components for the Trace Agent -### [comp/trace/agent](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/trace/agent) +### [comp/trace/agent](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/trace/agent) -### [comp/trace/config](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/trace/config) +### [comp/trace/config](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/trace/config) Package config implements a component to handle trace-agent configuration. This component temporarily wraps pkg/trace/config. diff --git a/tasks/components.py b/tasks/components.py index 5a25f9171a4d3..71430efcc2f53 100644 --- a/tasks/components.py +++ b/tasks/components.py @@ -138,7 +138,7 @@ def get_components_and_bundles(ctx): def make_components_md(bundles): - pkg_root = 'github.com/DataDog/dd-agent-comp-experiments/' + pkg_root = 'github.com/DataDog/datadog-agent/' yield '# Agent Components' yield '' yield '' From d13e6ec63384009ad57b3c2a1c77fb8d17fcf1fb Mon Sep 17 00:00:00 2001 From: Gustavo Caso Date: Wed, 29 Nov 2023 15:30:10 +0100 Subject: [PATCH 73/87] Compute apmStats in the server. (#21067) - Handle communicating with the trace-agent in the status package - Update the GUI to rende the apmStats using the informatoin from the server rather than using AJAX --- cmd/agent/gui/views/private/js/apm.js | 52 ---------------- cmd/agent/gui/views/private/js/ejs.min.js | 1 - cmd/agent/gui/views/private/js/javascript.js | 24 +------- .../gui/views/templates/generalStatus.tmpl | 59 ++++++++++++++++++- cmd/agent/gui/views/templates/index.tmpl | 1 - cmd/agent/subcommands/status/command.go | 34 ----------- pkg/status/apm/apm.go | 59 +++++++++++++++++++ pkg/status/status.go | 2 + 8 files changed, 118 insertions(+), 114 deletions(-) delete mode 100644 cmd/agent/gui/views/private/js/apm.js delete mode 100644 cmd/agent/gui/views/private/js/ejs.min.js create mode 100644 pkg/status/apm/apm.go diff --git a/cmd/agent/gui/views/private/js/apm.js b/cmd/agent/gui/views/private/js/apm.js deleted file mode 100644 index 6bf7a59d93ac3..0000000000000 --- a/cmd/agent/gui/views/private/js/apm.js +++ /dev/null @@ -1,52 +0,0 @@ -// apmTemplate defines the template which will be displayed on the APM section of the status page. -var apmTemplate = '' + -' Status: Running
    ' + -' Pid: <%= pid %>
    ' + -' Uptime: <%= uptime %> seconds
    ' + -' Mem alloc: <%= memstats.Alloc %> bytes
    ' + -' Hostname: <%= config.Hostname %>
    ' + -' Receiver: <%= config.ReceiverHost %>:<%= config.ReceiverPort %>
    ' + -' Endpoints:' + -' ' + -' <% config.Endpoints.forEach(function(e) { %>' + -' <%= e.Host %>
    ' + -' <% }); %>' + -'
    ' + -' Receiver (previous minute)' + -' ' + -' <% if (!Array.isArray(receiver) || receiver.length == 0) { %>' + -' No traces received in the previous minute.
    ' + -' <% } else { %>' + -' <% receiver.forEach(function(ts, i) { %>' + -' From <% if (typeof ts.Lang != "undefined") { %>' + -' <%= ts.Lang %> <%= ts.LangVersion %> (<%= ts.Interpreter %>), client <%= ts.TracerVersion %>' + -' <% } else { %>' + -' unknown clients' + -' <% } %>' + -' ' + -' Traces received: <%= ts.TracesReceived %> (<%= ts.TracesBytes %> bytes)
    ' + -' Spans received: <%= ts.SpansReceived %>' + -' <% if (typeof ts.WarnString != "undefined") { %>' + -'
    WARNING: <%= ts.WarnString %>
    ' + -' <% } %>' + -'
    ' + -' <% }); %>' + -' <% } %>' + -'' + -' <% if (typeof ratebyservice != "undefined" && ratebyservice != null) { %>' + -' <% Object.entries(ratebyservice).forEach(function(prop) { %>' + -' <% if (prop[0] == "service:,env:") { %>' + -' Default priority sampling rate: <%= prop[1].toFixed(2)*100 %>%' + -' <% } else { %>' + -' Priority sampling rate for \'<%= prop[0] %>\': <% prop[1].toFixed(2)*100 %>%' + -' <% } %>' + -' <% }); %>' + -' <% } %>' + -'
    ' + -' Writer (previous minute)' + -' ' + -' Traces: <%= trace_writer.Payloads %> payloads, <%= trace_writer.Traces %> traces, <%= trace_writer.Events %> events, <%= trace_writer.Bytes %> bytes
    ' + -' <% if (trace_writer.Errors > 0.0) { %>WARNING: Traces API errors (1 min): <%= trace_writer.Errors %><% } %>' + -' Stats: <%= stats_writer.Payloads %> payloads, <%= stats_writer.StatsBuckets %> stats buckets, <%= stats_writer.Bytes %> bytes
    ' + -' <% if (stats_writer.Errors > 0.0) { %>WARNING: Stats API errors (1 min): <%= stats_writer.Errors %><% } %>' + -'
    '; diff --git a/cmd/agent/gui/views/private/js/ejs.min.js b/cmd/agent/gui/views/private/js/ejs.min.js deleted file mode 100644 index 9e86b9697acc5..0000000000000 --- a/cmd/agent/gui/views/private/js/ejs.min.js +++ /dev/null @@ -1 +0,0 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ejs=f()}})(function(){var define,module,exports;return function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o1;if(options.cache){if(!filename){throw new Error("cache option requires a filename")}func=exports.cache.get(filename);if(func){return func}if(!hasTemplate){template=fileLoader(filename).toString().replace(_BOM,"")}}else if(!hasTemplate){if(!filename){throw new Error("Internal EJS error: no file name or template "+"provided")}template=fileLoader(filename).toString().replace(_BOM,"")}func=exports.compile(template,options);if(options.cache){exports.cache.set(filename,func)}return func}function tryHandleCache(options,data,cb){var result;if(!cb){if(typeof exports.promiseImpl=="function"){return new exports.promiseImpl(function(resolve,reject){try{result=handleCache(options)(data);resolve(result)}catch(err){reject(err)}})}else{throw new Error("Please provide a callback function")}}else{try{result=handleCache(options)(data)}catch(err){return cb(err)}cb(null,result)}}function fileLoader(filePath){return exports.fileLoader(filePath)}function includeFile(path,options){var opts=utils.shallowCopy({},options);opts.filename=getIncludePath(path,opts);return handleCache(opts)}function rethrow(err,str,flnm,lineno,esc){var lines=str.split("\n");var start=Math.max(lineno-3,0);var end=Math.min(lines.length,lineno+3);var filename=esc(flnm);var context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" >> ":" ")+curr+"| "+line}).join("\n");err.path=filename;err.message=(filename||"ejs")+":"+lineno+"\n"+context+"\n\n"+err.message;throw err}function stripSemi(str){return str.replace(/;(\s*$)/,"$1")}exports.compile=function compile(template,opts){var templ;if(opts&&opts.scope){if(!scopeOptionWarned){console.warn("`scope` option is deprecated and will be removed in EJS 3");scopeOptionWarned=true}if(!opts.context){opts.context=opts.scope}delete opts.scope}templ=new Template(template,opts);return templ.compile()};exports.render=function(template,d,o){var data=d||{};var opts=o||{};if(arguments.length==2){utils.shallowCopyFromList(opts,data,_OPTS_PASSABLE_WITH_DATA)}return handleCache(opts,template)(data)};exports.renderFile=function(){var args=Array.prototype.slice.call(arguments);var filename=args.shift();var cb;var opts={filename:filename};var data;var viewOpts;if(typeof arguments[arguments.length-1]=="function"){cb=args.pop()}if(args.length){data=args.shift();if(args.length){utils.shallowCopy(opts,args.pop())}else{if(data.settings){if(data.settings.views){opts.views=data.settings.views}if(data.settings["view cache"]){opts.cache=true}viewOpts=data.settings["view options"];if(viewOpts){utils.shallowCopy(opts,viewOpts)}}utils.shallowCopyFromList(opts,data,_OPTS_PASSABLE_WITH_DATA_EXPRESS)}opts.filename=filename}else{data={}}return tryHandleCache(opts,data,cb)};exports.Template=Template;exports.clearCache=function(){exports.cache.reset()};function Template(text,opts){opts=opts||{};var options={};this.templateText=text;this.mode=null;this.truncate=false;this.currentLine=1;this.source="";options.client=opts.client||false;options.escapeFunction=opts.escape||opts.escapeFunction||utils.escapeXML;options.compileDebug=opts.compileDebug!==false;options.debug=!!opts.debug;options.filename=opts.filename;options.openDelimiter=opts.openDelimiter||exports.openDelimiter||_DEFAULT_OPEN_DELIMITER;options.closeDelimiter=opts.closeDelimiter||exports.closeDelimiter||_DEFAULT_CLOSE_DELIMITER;options.delimiter=opts.delimiter||exports.delimiter||_DEFAULT_DELIMITER;options.strict=opts.strict||false;options.context=opts.context;options.cache=opts.cache||false;options.rmWhitespace=opts.rmWhitespace;options.root=opts.root;options.outputFunctionName=opts.outputFunctionName;options.localsName=opts.localsName||exports.localsName||_DEFAULT_LOCALS_NAME;options.views=opts.views;options.async=opts.async;options.destructuredLocals=opts.destructuredLocals;options.legacyInclude=typeof opts.legacyInclude!="undefined"?!!opts.legacyInclude:true;if(options.strict){options._with=false}else{options._with=typeof opts._with!="undefined"?opts._with:true}this.opts=options;this.regex=this.createRegex()}Template.modes={EVAL:"eval",ESCAPED:"escaped",RAW:"raw",COMMENT:"comment",LITERAL:"literal"};Template.prototype={createRegex:function(){var str=_REGEX_STRING;var delim=utils.escapeRegExpChars(this.opts.delimiter);var open=utils.escapeRegExpChars(this.opts.openDelimiter);var close=utils.escapeRegExpChars(this.opts.closeDelimiter);str=str.replace(/%/g,delim).replace(//g,close);return new RegExp(str)},compile:function(){var src;var fn;var opts=this.opts;var prepended="";var appended="";var escapeFn=opts.escapeFunction;var ctor;if(!this.source){this.generateSource();prepended+=' var __output = "";\n'+" function __append(s) { if (s !== undefined && s !== null) __output += s }\n";if(opts.outputFunctionName){prepended+=" var "+opts.outputFunctionName+" = __append;"+"\n"}if(opts.destructuredLocals&&opts.destructuredLocals.length){var destructuring=" var __locals = ("+opts.localsName+" || {}),\n";for(var i=0;i0){destructuring+=",\n "}destructuring+=name+" = __locals."+name}prepended+=destructuring+";\n"}if(opts._with!==false){prepended+=" with ("+opts.localsName+" || {}) {"+"\n";appended+=" }"+"\n"}appended+=" return __output;"+"\n";this.source=prepended+this.source+appended}if(opts.compileDebug){src="var __line = 1"+"\n"+" , __lines = "+JSON.stringify(this.templateText)+"\n"+" , __filename = "+(opts.filename?JSON.stringify(opts.filename):"undefined")+";"+"\n"+"try {"+"\n"+this.source+"} catch (e) {"+"\n"+" rethrow(e, __lines, __filename, __line, escapeFn);"+"\n"+"}"+"\n"}else{src=this.source}if(opts.client){src="escapeFn = escapeFn || "+escapeFn.toString()+";"+"\n"+src;if(opts.compileDebug){src="rethrow = rethrow || "+rethrow.toString()+";"+"\n"+src}}if(opts.strict){src='"use strict";\n'+src}if(opts.debug){console.log(src)}if(opts.compileDebug&&opts.filename){src=src+"\n"+"//# sourceURL="+opts.filename+"\n"}try{if(opts.async){try{ctor=new Function("return (async function(){}).constructor;")()}catch(e){if(e instanceof SyntaxError){throw new Error("This environment does not support async/await")}else{throw e}}}else{ctor=Function}fn=new ctor(opts.localsName+", escapeFn, include, rethrow",src)}catch(e){if(e instanceof SyntaxError){if(opts.filename){e.message+=" in "+opts.filename}e.message+=" while compiling ejs\n\n";e.message+="If the above error is not helpful, you may want to try EJS-Lint:\n";e.message+="https://github.com/RyanZim/EJS-Lint";if(!opts.async){e.message+="\n";e.message+="Or, if you meant to create an async function, pass `async: true` as an option."}}throw e}var returnedFn=opts.client?fn:function anonymous(data){var include=function(path,includeData){var d=utils.shallowCopy({},data);if(includeData){d=utils.shallowCopy(d,includeData)}return includeFile(path,opts)(d)};return fn.apply(opts.context,[data||{},escapeFn,include,rethrow])};if(opts.filename&&typeof Object.defineProperty==="function"){var filename=opts.filename;var basename=path.basename(filename,path.extname(filename));try{Object.defineProperty(returnedFn,"name",{value:basename,writable:false,enumerable:false,configurable:true})}catch(e){}}return returnedFn},generateSource:function(){var opts=this.opts;if(opts.rmWhitespace){this.templateText=this.templateText.replace(/[\r\n]+/g,"\n").replace(/^\s+|\s+$/gm,"")}this.templateText=this.templateText.replace(/[ \t]*<%_/gm,"<%_").replace(/_%>[ \t]*/gm,"_%>");var self=this;var matches=this.parseTemplateText();var d=this.opts.delimiter;var o=this.opts.openDelimiter;var c=this.opts.closeDelimiter;if(matches&&matches.length){matches.forEach(function(line,index){var closing;if(line.indexOf(o+d)===0&&line.indexOf(o+d+d)!==0){closing=matches[index+2];if(!(closing==d+c||closing=="-"+d+c||closing=="_"+d+c)){throw new Error('Could not find matching close tag for "'+line+'".')}}self.scanLine(line)})}},parseTemplateText:function(){var str=this.templateText;var pat=this.regex;var result=pat.exec(str);var arr=[];var firstPos;while(result){firstPos=result.index;if(firstPos!==0){arr.push(str.substring(0,firstPos));str=str.slice(firstPos)}arr.push(result[0]);str=str.slice(result[0].length);result=pat.exec(str)}if(str){arr.push(str)}return arr},_addOutput:function(line){if(this.truncate){line=line.replace(/^(?:\r\n|\r|\n)/,"");this.truncate=false}if(!line){return line}line=line.replace(/\\/g,"\\\\");line=line.replace(/\n/g,"\\n");line=line.replace(/\r/g,"\\r");line=line.replace(/"/g,'\\"');this.source+=' ; __append("'+line+'")'+"\n"},scanLine:function(line){var self=this;var d=this.opts.delimiter;var o=this.opts.openDelimiter;var c=this.opts.closeDelimiter;var newLineCount=0;newLineCount=line.split("\n").length-1;switch(line){case o+d:case o+d+"_":this.mode=Template.modes.EVAL;break;case o+d+"=":this.mode=Template.modes.ESCAPED;break;case o+d+"-":this.mode=Template.modes.RAW;break;case o+d+"#":this.mode=Template.modes.COMMENT;break;case o+d+d:this.mode=Template.modes.LITERAL;this.source+=' ; __append("'+line.replace(o+d+d,o+d)+'")'+"\n";break;case d+d+c:this.mode=Template.modes.LITERAL;this.source+=' ; __append("'+line.replace(d+d+c,d+c)+'")'+"\n";break;case d+c:case"-"+d+c:case"_"+d+c:if(this.mode==Template.modes.LITERAL){this._addOutput(line)}this.mode=null;this.truncate=line.indexOf("-")===0||line.indexOf("_")===0;break;default:if(this.mode){switch(this.mode){case Template.modes.EVAL:case Template.modes.ESCAPED:case Template.modes.RAW:if(line.lastIndexOf("//")>line.lastIndexOf("\n")){line+="\n"}}switch(this.mode){case Template.modes.EVAL:this.source+=" ; "+line+"\n";break;case Template.modes.ESCAPED:this.source+=" ; __append(escapeFn("+stripSemi(line)+"))"+"\n";break;case Template.modes.RAW:this.source+=" ; __append("+stripSemi(line)+")"+"\n";break;case Template.modes.COMMENT:break;case Template.modes.LITERAL:this._addOutput(line);break}}else{this._addOutput(line)}}if(self.opts.compileDebug&&newLineCount){this.currentLine+=newLineCount;this.source+=" ; __line = "+this.currentLine+"\n"}}};exports.escapeXML=utils.escapeXML;exports.__express=exports.renderFile;exports.VERSION=_VERSION_STRING;exports.name=_NAME;if(typeof window!="undefined"){window.ejs=exports}},{"../package.json":6,"./utils":2,fs:3,path:4}],2:[function(require,module,exports){"use strict";var regExpChars=/[|\\{}()[\]^$+*?.]/g;exports.escapeRegExpChars=function(string){if(!string){return""}return String(string).replace(regExpChars,"\\$&")};var _ENCODE_HTML_RULES={"&":"&","<":"<",">":">",'"':""","'":"'"};var _MATCH_HTML=/[&<>'"]/g;function encode_char(c){return _ENCODE_HTML_RULES[c]||c}var escapeFuncStr="var _ENCODE_HTML_RULES = {\n"+' "&": "&"\n'+' , "<": "<"\n'+' , ">": ">"\n'+' , \'"\': """\n'+' , "\'": "'"\n'+" }\n"+" , _MATCH_HTML = /[&<>'\"]/g;\n"+"function encode_char(c) {\n"+" return _ENCODE_HTML_RULES[c] || c;\n"+"};\n";exports.escapeXML=function(markup){return markup==undefined?"":String(markup).replace(_MATCH_HTML,encode_char)};exports.escapeXML.toString=function(){return Function.prototype.toString.call(this)+";\n"+escapeFuncStr};exports.shallowCopy=function(to,from){from=from||{};for(var p in from){to[p]=from[p]}return to};exports.shallowCopyFromList=function(to,from,list){for(var i=0;i=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up--;up){parts.unshift("..")}}return parts}var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;var splitPath=function(filename){return splitPathRe.exec(filename).slice(1)};exports.resolve=function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:process.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){continue}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=normalizeArray(filter(resolvedPath.split("/"),function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."};exports.normalize=function(path){var isAbsolute=exports.isAbsolute(path),trailingSlash=substr(path,-1)==="/";path=normalizeArray(filter(path.split("/"),function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path};exports.isAbsolute=function(path){return path.charAt(0)==="/"};exports.join=function(){var paths=Array.prototype.slice.call(arguments,0);return exports.normalize(filter(paths,function(p,index){if(typeof p!=="string"){throw new TypeError("Arguments to path.join must be strings")}return p}).join("/"))};exports.relative=function(from,to){from=exports.resolve(from).substr(1);to=exports.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i1){for(var i=1;i (http://fleegix.org)",license:"Apache-2.0",main:"./lib/ejs.js",repository:{type:"git",url:"git://github.com/mde/ejs.git"},bugs:"https://github.com/mde/ejs/issues",homepage:"https://github.com/mde/ejs",dependencies:{},devDependencies:{browserify:"^13.1.1",eslint:"^4.14.0","git-directory-deploy":"^1.5.1",jake:"^10.3.1",jsdoc:"^3.4.0","lru-cache":"^4.0.1",mocha:"^5.0.5","uglify-js":"^3.3.16"},engines:{node:">=0.10.0"},scripts:{test:"mocha",postinstall:"node --harmony ./postinstall.js"}}},{}]},{},[1])(1)}); diff --git a/cmd/agent/gui/views/private/js/javascript.js b/cmd/agent/gui/views/private/js/javascript.js index 66d800a586e34..d8c6c9eb25ed6 100644 --- a/cmd/agent/gui/views/private/js/javascript.js +++ b/cmd/agent/gui/views/private/js/javascript.js @@ -124,7 +124,7 @@ function checkStatus() { if (checkStatus.uptime > last_ts) { $("#restart_status").hide() } - checkStatus.uptime = last_ts + checkStatus.uptime = last_ts },function() { $("#agent_status").html("Not connected
    to Agent"); $("#agent_status").css({ @@ -153,28 +153,6 @@ function loadStatus(page) { sendMessage("agent/status/" + page, "", "post", function(data, status, xhr){ $("#" + page + "_status").html(DOMPurify.sanitize(data)); - - // Get the trace-agent status - sendMessage("agent/getConfig/apm_config.receiver_port", "", "GET", - function(data, status, xhr) { - var apmPort = data["apm_config.debug.port"]; - if (apmPort == null) { - apmPort = "5012"; - } - var url = "http://127.0.0.1:"+apmPort+"/debug/vars" - $.ajax({ - url: url, - type: "GET", - success: function(data) { - $("#apmStats > .stat_data").html(ejs.render(apmTemplate, data)); - }, - error: function() { - $("#apmStats > .stat_data").text("Status: Not running or not on localhost."); - } - }) - }, function() { - $("#apmStats > .stat_data").html("Could not obtain trace-agent port from API."); - }) },function(){ $("#" + page + "_status").html("An error occurred."); }); diff --git a/cmd/agent/gui/views/templates/generalStatus.tmpl b/cmd/agent/gui/views/templates/generalStatus.tmpl index fd993865c4cdc..d60bcc18466cd 100644 --- a/cmd/agent/gui/views/templates/generalStatus.tmpl +++ b/cmd/agent/gui/views/templates/generalStatus.tmpl @@ -478,9 +478,61 @@ -
    +
    APM - Loading... + + {{- with .apmStats -}} + {{- if .error }} + Not running or unreachable on localhost:{{.port}}
    + Error: {{.error}}
    + {{- else}} + Status: Running
    + Pid: {{.pid}}
    + Uptime: {{.uptime}} seconds
    + Mem alloc: {{humanize .memstats.Alloc}} bytes
    + Hostname: {{.config.Hostname}}
    + Receiver: {{.config.ReceiverHost}}:{{.config.ReceiverPort}}
    + Endpoints: + + {{- range $i, $e := .config.Endpoints}} + {{ $e.Host }}
    + {{- end }} +
    + Receiver (previous minute) + + {{- if eq (len .receiver) 0}} + No traces received in the previous minute.
    + {{ else }} + {{ range $i, $ts := .receiver }} + From {{if $ts.Lang}}{{ $ts.Lang }} {{ $ts.LangVersion }} ({{ $ts.Interpreter }}), client {{ $ts.TracerVersion }}{{else}}unknown clients{{end}} + + Traces received: {{ $ts.TracesReceived }} ({{ humanize $ts.TracesBytes }} bytes)
    + Spans received: {{ $ts.SpansReceived }} + {{ with $ts.WarnString }} +
    WARNING: {{ . }}
    + {{ end }} +
    + {{ end }} + {{ end }} + + {{range $key, $value := .ratebyservice_filtered -}} + {{- if eq $key "service:,env:" -}} + Default priority sampling rate: {{percent $value}}% + {{- else}} + Priority sampling rate for '{{ $key }}': {{percent $value}}% + {{- end}} + {{- end }} +
    + Writer (previous minute) + + Traces: {{.trace_writer.Payloads}} payloads, {{.trace_writer.Traces}} traces, {{.trace_writer.Events}} events, {{humanize .trace_writer.Bytes}} bytes
    + {{- if gt .trace_writer.Errors 0.0}}WARNING: Traces API errors (1 min): {{.trace_writer.Errors}}{{end}} + Stats: {{.stats_writer.Payloads}} payloads, {{.stats_writer.StatsBuckets}} stats buckets, {{humanize .stats_writer.Bytes}} bytes
    + {{- if gt .stats_writer.Errors 0.0}}WARNING: Stats API errors (1 min): {{.stats_writer.Errors}}{{end}} +
    + {{- end }} + {{ end }} +
    @@ -532,7 +584,8 @@ {{- end }} {{ end }} -
    +
    +
    diff --git a/cmd/agent/gui/views/templates/index.tmpl b/cmd/agent/gui/views/templates/index.tmpl index 78723c4b4e7b4..daed6e5788e09 100644 --- a/cmd/agent/gui/views/templates/index.tmpl +++ b/cmd/agent/gui/views/templates/index.tmpl @@ -8,7 +8,6 @@ - diff --git a/cmd/agent/subcommands/status/command.go b/cmd/agent/subcommands/status/command.go index 88b30ee873dc1..a86a5d61e0076 100644 --- a/cmd/agent/subcommands/status/command.go +++ b/cmd/agent/subcommands/status/command.go @@ -11,10 +11,8 @@ import ( "encoding/json" "errors" "fmt" - "net/http" "net/url" "os" - "time" "go.uber.org/fx" @@ -162,14 +160,6 @@ func requestStatus(config config.Component, cliParams *cliParams) error { if err != nil { return err } - // attach trace-agent status, if obtainable - temp := make(map[string]interface{}) - if err := json.Unmarshal(r, &temp); err == nil { - temp["apmStats"] = getAPMStatus(config) - if newr, err := json.Marshal(temp); err == nil { - r = newr - } - } // The rendering is done in the client so that the agent has less work to do if cliParams.prettyPrintJSON { @@ -195,30 +185,6 @@ func requestStatus(config config.Component, cliParams *cliParams) error { return nil } -// getAPMStatus returns a set of key/value pairs summarizing the status of the trace-agent. -// If the status can not be obtained for any reason, the returned map will contain an "error" -// key with an explanation. -func getAPMStatus(config config.Component) map[string]interface{} { - port := config.GetInt("apm_config.debug.port") - url := fmt.Sprintf("http://localhost:%d/debug/vars", port) - resp, err := (&http.Client{Timeout: 2 * time.Second}).Get(url) - if err != nil { - return map[string]interface{}{ - "port": port, - "error": err.Error(), - } - } - defer resp.Body.Close() - status := make(map[string]interface{}) - if err := json.NewDecoder(resp.Body).Decode(&status); err != nil { - return map[string]interface{}{ - "port": port, - "error": err.Error(), - } - } - return status -} - func componentStatusCmd(log log.Component, config config.Component, cliParams *cliParams) error { if len(cliParams.args) != 1 { return fmt.Errorf("a component name must be specified") diff --git a/pkg/status/apm/apm.go b/pkg/status/apm/apm.go new file mode 100644 index 0000000000000..e5576ded12793 --- /dev/null +++ b/pkg/status/apm/apm.go @@ -0,0 +1,59 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package apm fetch information about the apm agent. +// This will, in time, be migrated to the apm agent component. +package apm + +import ( + "encoding/json" + "fmt" + "net/http" + "sync" + + apiutil "github.com/DataDog/datadog-agent/pkg/api/util" + "github.com/DataDog/datadog-agent/pkg/config" +) + +// httpClients should be reused instead of created as needed. They keep cached TCP connections +// that may leak otherwise +var ( + httpClient *http.Client + clientInitOnce sync.Once +) + +func client() *http.Client { + clientInitOnce.Do(func() { + httpClient = apiutil.GetClient(false) + }) + + return httpClient +} + +// GetAPMStatus returns a set of key/value pairs summarizing the status of the trace-agent. +// If the status can not be obtained for any reason, the returned map will contain an "error" +// key with an explanation. +func GetAPMStatus() map[string]interface{} { + port := config.Datadog.GetInt("apm_config.debug.port") + + c := client() + url := fmt.Sprintf("http://localhost:%d/debug/vars", port) + resp, err := apiutil.DoGet(c, url, apiutil.CloseConnection) + if err != nil { + return map[string]interface{}{ + "port": port, + "error": err.Error(), + } + } + + status := make(map[string]interface{}) + if err := json.Unmarshal(resp, &status); err != nil { + return map[string]interface{}{ + "port": port, + "error": err.Error(), + } + } + return status +} diff --git a/pkg/status/status.go b/pkg/status/status.go index 21696dc0ac489..a3f86b0c6cabb 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -30,6 +30,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/config/utils" logsStatus "github.com/DataDog/datadog-agent/pkg/logs/status" "github.com/DataDog/datadog-agent/pkg/snmp/traps" + "github.com/DataDog/datadog-agent/pkg/status/apm" "github.com/DataDog/datadog-agent/pkg/status/collector" "github.com/DataDog/datadog-agent/pkg/status/jmx" "github.com/DataDog/datadog-agent/pkg/status/otlp" @@ -82,6 +83,7 @@ func GetStatus(verbose bool, invAgent inventoryagent.Component) (map[string]inte } stats["processAgentStatus"] = GetProcessAgentStatus() + stats["apmStats"] = apm.GetAPMStatus() if !config.Datadog.GetBool("no_proxy_nonexact_match") { stats["TransportWarnings"] = httputils.GetNumberOfWarnings() > 0 From c8cfd6ee899beee3c6cf2a4f0e21bf3bbd77aee1 Mon Sep 17 00:00:00 2001 From: Sylvain Afchain Date: Wed, 29 Nov 2023 15:47:25 +0100 Subject: [PATCH 74/87] [CWS] remove static defined field from generator (#21175) --- .../generators/accessors/accessors.go | 4 +- .../generators/accessors/field_accessors.tmpl | 7 +- .../secl/model/field_accessors_unix.go | 6041 +++++++---------- .../secl/model/field_accessors_windows.go | 317 +- 4 files changed, 2504 insertions(+), 3865 deletions(-) diff --git a/pkg/security/secl/compiler/generators/accessors/accessors.go b/pkg/security/secl/compiler/generators/accessors/accessors.go index 32595b8790657..6b333945acb3f 100644 --- a/pkg/security/secl/compiler/generators/accessors/accessors.go +++ b/pkg/security/secl/compiler/generators/accessors/accessors.go @@ -740,7 +740,7 @@ func newField(allFields map[string]*common.StructField, field *common.StructFiel return result } -func generatePrefixNilChecks(allFields map[string]*common.StructField, field *common.StructField) string { +func generatePrefixNilChecks(allFields map[string]*common.StructField, returnType string, field *common.StructField) string { var fieldPath, result string for _, node := range strings.Split(field.Name, ".") { if fieldPath != "" { @@ -751,7 +751,7 @@ func generatePrefixNilChecks(allFields map[string]*common.StructField, field *co if field, ok := allFields[fieldPath]; ok { if field.IsOrigTypePtr { - result += fmt.Sprintf("if ev.%s == nil { return zeroValue }\n", field.Name) + result += fmt.Sprintf("if ev.%s == nil { return %s }\n", field.Name, getDefaultValueOfType(returnType)) } } } diff --git a/pkg/security/secl/compiler/generators/accessors/field_accessors.tmpl b/pkg/security/secl/compiler/generators/accessors/field_accessors.tmpl index eade58343fb69..407a122f4aaa3 100644 --- a/pkg/security/secl/compiler/generators/accessors/field_accessors.tmpl +++ b/pkg/security/secl/compiler/generators/accessors/field_accessors.tmpl @@ -32,16 +32,13 @@ import ( // Get{{$pascalCaseName}} returns the value of the field, resolving if necessary func (ev *Event) Get{{$pascalCaseName}}() {{ $accessorReturnType }} { - {{ if and (ne $Field.Handler "ResolveAsync") (and (ne $Field.Handler "ResolveEventTimestamp") (ne $Field.Handler "ResolveEventTime")) }} - zeroValue := {{ GetDefaultValueOfType $accessorReturnType}} - {{ end }} {{if ne $Field.Event "*"}} if ev.GetEventType().String() != "{{$Field.Event}}" { - return zeroValue + return {{ GetDefaultValueOfType $accessorReturnType}} } {{end}} - {{$Field | GeneratePrefixNilChecks $.AllFields}} + {{$Field | GeneratePrefixNilChecks $.AllFields $accessorReturnType}} {{if $Field.Iterator}} var values {{ $accessorReturnType }} diff --git a/pkg/security/secl/model/field_accessors_unix.go b/pkg/security/secl/model/field_accessors_unix.go index d091b71b2bbb4..5597942d65c5c 100644 --- a/pkg/security/secl/model/field_accessors_unix.go +++ b/pkg/security/secl/model/field_accessors_unix.go @@ -17,657 +17,584 @@ import ( // GetBindAddrFamily returns the value of the field, resolving if necessary func (ev *Event) GetBindAddrFamily() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "bind" { - return zeroValue + return uint16(0) } return ev.Bind.AddrFamily } // GetBindAddrIp returns the value of the field, resolving if necessary func (ev *Event) GetBindAddrIp() net.IPNet { - zeroValue := net.IPNet{} if ev.GetEventType().String() != "bind" { - return zeroValue + return net.IPNet{} } return ev.Bind.Addr.IPNet } // GetBindAddrPort returns the value of the field, resolving if necessary func (ev *Event) GetBindAddrPort() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "bind" { - return zeroValue + return uint16(0) } return ev.Bind.Addr.Port } // GetBindRetval returns the value of the field, resolving if necessary func (ev *Event) GetBindRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "bind" { - return zeroValue + return int64(0) } return ev.Bind.SyscallEvent.Retval } // GetBpfCmd returns the value of the field, resolving if necessary func (ev *Event) GetBpfCmd() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "bpf" { - return zeroValue + return uint32(0) } return ev.BPF.Cmd } // GetBpfMapName returns the value of the field, resolving if necessary func (ev *Event) GetBpfMapName() string { - zeroValue := "" if ev.GetEventType().String() != "bpf" { - return zeroValue + return "" } return ev.BPF.Map.Name } // GetBpfMapType returns the value of the field, resolving if necessary func (ev *Event) GetBpfMapType() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "bpf" { - return zeroValue + return uint32(0) } return ev.BPF.Map.Type } // GetBpfProgAttachType returns the value of the field, resolving if necessary func (ev *Event) GetBpfProgAttachType() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "bpf" { - return zeroValue + return uint32(0) } return ev.BPF.Program.AttachType } // GetBpfProgHelpers returns the value of the field, resolving if necessary func (ev *Event) GetBpfProgHelpers() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "bpf" { - return zeroValue + return []uint32{} } return ev.BPF.Program.Helpers } // GetBpfProgName returns the value of the field, resolving if necessary func (ev *Event) GetBpfProgName() string { - zeroValue := "" if ev.GetEventType().String() != "bpf" { - return zeroValue + return "" } return ev.BPF.Program.Name } // GetBpfProgTag returns the value of the field, resolving if necessary func (ev *Event) GetBpfProgTag() string { - zeroValue := "" if ev.GetEventType().String() != "bpf" { - return zeroValue + return "" } return ev.BPF.Program.Tag } // GetBpfProgType returns the value of the field, resolving if necessary func (ev *Event) GetBpfProgType() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "bpf" { - return zeroValue + return uint32(0) } return ev.BPF.Program.Type } // GetBpfRetval returns the value of the field, resolving if necessary func (ev *Event) GetBpfRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "bpf" { - return zeroValue + return int64(0) } return ev.BPF.SyscallEvent.Retval } // GetCapsetCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetCapsetCapEffective() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "capset" { - return zeroValue + return uint64(0) } return ev.Capset.CapEffective } // GetCapsetCapPermitted returns the value of the field, resolving if necessary func (ev *Event) GetCapsetCapPermitted() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "capset" { - return zeroValue + return uint64(0) } return ev.Capset.CapPermitted } // GetChmodFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "chmod" { - return zeroValue + return uint64(0) } return ev.Chmod.File.FileFields.CTime } // GetChmodFileDestinationMode returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileDestinationMode() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "chmod" { - return zeroValue + return uint32(0) } return ev.Chmod.Mode } // GetChmodFileDestinationRights returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileDestinationRights() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "chmod" { - return zeroValue + return uint32(0) } return ev.Chmod.Mode } // GetChmodFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "chmod" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.Chmod.File) } // GetChmodFileGid returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "chmod" { - return zeroValue + return uint32(0) } return ev.Chmod.File.FileFields.GID } // GetChmodFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "chmod" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Chmod.File.FileFields) } // GetChmodFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "chmod" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Chmod.File) } // GetChmodFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "chmod" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.Chmod.File.FileFields) } // GetChmodFileInode returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "chmod" { - return zeroValue + return uint64(0) } return ev.Chmod.File.FileFields.PathKey.Inode } // GetChmodFileMode returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "chmod" { - return zeroValue + return uint16(0) } return ev.Chmod.File.FileFields.Mode } // GetChmodFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "chmod" { - return zeroValue + return uint64(0) } return ev.Chmod.File.FileFields.MTime } // GetChmodFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "chmod" { - return zeroValue + return uint32(0) } return ev.Chmod.File.FileFields.PathKey.MountID } // GetChmodFileName returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileName() string { - zeroValue := "" if ev.GetEventType().String() != "chmod" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Chmod.File) } // GetChmodFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "chmod" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Chmod.File)) } // GetChmodFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetChmodFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "chmod" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.Chmod.File) } // GetChmodFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetChmodFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "chmod" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.Chmod.File) } // GetChmodFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetChmodFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "chmod" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.Chmod.File) } // GetChmodFilePath returns the value of the field, resolving if necessary func (ev *Event) GetChmodFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "chmod" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Chmod.File) } // GetChmodFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetChmodFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "chmod" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Chmod.File)) } // GetChmodFileRights returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "chmod" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.Chmod.File.FileFields) } // GetChmodFileUid returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "chmod" { - return zeroValue + return uint32(0) } return ev.Chmod.File.FileFields.UID } // GetChmodFileUser returns the value of the field, resolving if necessary func (ev *Event) GetChmodFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "chmod" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Chmod.File.FileFields) } // GetChmodRetval returns the value of the field, resolving if necessary func (ev *Event) GetChmodRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "chmod" { - return zeroValue + return int64(0) } return ev.Chmod.SyscallEvent.Retval } // GetChownFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetChownFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "chown" { - return zeroValue + return uint64(0) } return ev.Chown.File.FileFields.CTime } // GetChownFileDestinationGid returns the value of the field, resolving if necessary func (ev *Event) GetChownFileDestinationGid() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "chown" { - return zeroValue + return int64(0) } return ev.Chown.GID } // GetChownFileDestinationGroup returns the value of the field, resolving if necessary func (ev *Event) GetChownFileDestinationGroup() string { - zeroValue := "" if ev.GetEventType().String() != "chown" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveChownGID(ev, &ev.Chown) } // GetChownFileDestinationUid returns the value of the field, resolving if necessary func (ev *Event) GetChownFileDestinationUid() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "chown" { - return zeroValue + return int64(0) } return ev.Chown.UID } // GetChownFileDestinationUser returns the value of the field, resolving if necessary func (ev *Event) GetChownFileDestinationUser() string { - zeroValue := "" if ev.GetEventType().String() != "chown" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveChownUID(ev, &ev.Chown) } // GetChownFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetChownFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "chown" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.Chown.File) } // GetChownFileGid returns the value of the field, resolving if necessary func (ev *Event) GetChownFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "chown" { - return zeroValue + return uint32(0) } return ev.Chown.File.FileFields.GID } // GetChownFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetChownFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "chown" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Chown.File.FileFields) } // GetChownFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetChownFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "chown" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Chown.File) } // GetChownFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetChownFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "chown" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.Chown.File.FileFields) } // GetChownFileInode returns the value of the field, resolving if necessary func (ev *Event) GetChownFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "chown" { - return zeroValue + return uint64(0) } return ev.Chown.File.FileFields.PathKey.Inode } // GetChownFileMode returns the value of the field, resolving if necessary func (ev *Event) GetChownFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "chown" { - return zeroValue + return uint16(0) } return ev.Chown.File.FileFields.Mode } // GetChownFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetChownFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "chown" { - return zeroValue + return uint64(0) } return ev.Chown.File.FileFields.MTime } // GetChownFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetChownFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "chown" { - return zeroValue + return uint32(0) } return ev.Chown.File.FileFields.PathKey.MountID } // GetChownFileName returns the value of the field, resolving if necessary func (ev *Event) GetChownFileName() string { - zeroValue := "" if ev.GetEventType().String() != "chown" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Chown.File) } // GetChownFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetChownFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "chown" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Chown.File)) } // GetChownFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetChownFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "chown" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.Chown.File) } // GetChownFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetChownFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "chown" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.Chown.File) } // GetChownFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetChownFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "chown" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.Chown.File) } // GetChownFilePath returns the value of the field, resolving if necessary func (ev *Event) GetChownFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "chown" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Chown.File) } // GetChownFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetChownFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "chown" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Chown.File)) } // GetChownFileRights returns the value of the field, resolving if necessary func (ev *Event) GetChownFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "chown" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.Chown.File.FileFields) } // GetChownFileUid returns the value of the field, resolving if necessary func (ev *Event) GetChownFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "chown" { - return zeroValue + return uint32(0) } return ev.Chown.File.FileFields.UID } // GetChownFileUser returns the value of the field, resolving if necessary func (ev *Event) GetChownFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "chown" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Chown.File.FileFields) } // GetChownRetval returns the value of the field, resolving if necessary func (ev *Event) GetChownRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "chown" { - return zeroValue + return int64(0) } return ev.Chown.SyscallEvent.Retval } // GetContainerCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetContainerCreatedAt() int { - zeroValue := 0 if ev.BaseEvent.ContainerContext == nil { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveContainerCreatedAt(ev, ev.BaseEvent.ContainerContext) } // GetContainerId returns the value of the field, resolving if necessary func (ev *Event) GetContainerId() string { - zeroValue := "" if ev.BaseEvent.ContainerContext == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveContainerID(ev, ev.BaseEvent.ContainerContext) } // GetContainerTags returns the value of the field, resolving if necessary func (ev *Event) GetContainerTags() []string { - zeroValue := []string{} if ev.BaseEvent.ContainerContext == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveContainerTags(ev, ev.BaseEvent.ContainerContext) } // GetDnsId returns the value of the field, resolving if necessary func (ev *Event) GetDnsId() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "dns" { - return zeroValue + return uint16(0) } return ev.DNS.ID } // GetDnsQuestionClass returns the value of the field, resolving if necessary func (ev *Event) GetDnsQuestionClass() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "dns" { - return zeroValue + return uint16(0) } return ev.DNS.Class } // GetDnsQuestionCount returns the value of the field, resolving if necessary func (ev *Event) GetDnsQuestionCount() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "dns" { - return zeroValue + return uint16(0) } return ev.DNS.Count } // GetDnsQuestionLength returns the value of the field, resolving if necessary func (ev *Event) GetDnsQuestionLength() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "dns" { - return zeroValue + return uint16(0) } return ev.DNS.Size } // GetDnsQuestionName returns the value of the field, resolving if necessary func (ev *Event) GetDnsQuestionName() string { - zeroValue := "" if ev.GetEventType().String() != "dns" { - return zeroValue + return "" } return ev.DNS.Name } // GetDnsQuestionNameLength returns the value of the field, resolving if necessary func (ev *Event) GetDnsQuestionNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "dns" { - return zeroValue + return 0 } return len(ev.DNS.Name) } // GetDnsQuestionType returns the value of the field, resolving if necessary func (ev *Event) GetDnsQuestionType() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "dns" { - return zeroValue + return uint16(0) } return ev.DNS.Type } @@ -684,276 +611,253 @@ func (ev *Event) GetEventTimestamp() int { // GetExecArgs returns the value of the field, resolving if necessary func (ev *Event) GetExecArgs() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgs(ev, ev.Exec.Process) } // GetExecArgsFlags returns the value of the field, resolving if necessary func (ev *Event) GetExecArgsFlags() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exec" { - return zeroValue + return []string{} } if ev.Exec.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgsFlags(ev, ev.Exec.Process) } // GetExecArgsOptions returns the value of the field, resolving if necessary func (ev *Event) GetExecArgsOptions() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exec" { - return zeroValue + return []string{} } if ev.Exec.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgsOptions(ev, ev.Exec.Process) } // GetExecArgsScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetExecArgsScrubbed() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, ev.Exec.Process) } // GetExecArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetExecArgsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "exec" { - return zeroValue + return false } if ev.Exec.Process == nil { - return zeroValue + return false } return ev.FieldHandlers.ResolveProcessArgsTruncated(ev, ev.Exec.Process) } // GetExecArgv returns the value of the field, resolving if necessary func (ev *Event) GetExecArgv() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exec" { - return zeroValue + return []string{} } if ev.Exec.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgv(ev, ev.Exec.Process) } // GetExecArgv0 returns the value of the field, resolving if necessary func (ev *Event) GetExecArgv0() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgv0(ev, ev.Exec.Process) } // GetExecArgvScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetExecArgvScrubbed() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exec" { - return zeroValue + return []string{} } if ev.Exec.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.Exec.Process) } // GetExecCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetExecCapEffective() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint64(0) } if ev.Exec.Process == nil { - return zeroValue + return uint64(0) } return ev.Exec.Process.Credentials.CapEffective } // GetExecCapPermitted returns the value of the field, resolving if necessary func (ev *Event) GetExecCapPermitted() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint64(0) } if ev.Exec.Process == nil { - return zeroValue + return uint64(0) } return ev.Exec.Process.Credentials.CapPermitted } // GetExecComm returns the value of the field, resolving if necessary func (ev *Event) GetExecComm() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.Exec.Process.Comm } // GetExecContainerId returns the value of the field, resolving if necessary func (ev *Event) GetExecContainerId() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.Exec.Process.ContainerID } // GetExecCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetExecCreatedAt() int { - zeroValue := 0 if ev.GetEventType().String() != "exec" { - return zeroValue + return 0 } if ev.Exec.Process == nil { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveProcessCreatedAt(ev, ev.Exec.Process) } // GetExecEgid returns the value of the field, resolving if necessary func (ev *Event) GetExecEgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } return ev.Exec.Process.Credentials.EGID } // GetExecEgroup returns the value of the field, resolving if necessary func (ev *Event) GetExecEgroup() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.Exec.Process.Credentials.EGroup } // GetExecEnvp returns the value of the field, resolving if necessary func (ev *Event) GetExecEnvp() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exec" { - return zeroValue + return []string{} } if ev.Exec.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exec.Process) } // GetExecEnvs returns the value of the field, resolving if necessary func (ev *Event) GetExecEnvs() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exec" { - return zeroValue + return []string{} } if ev.Exec.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exec.Process) } // GetExecEnvsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetExecEnvsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "exec" { - return zeroValue + return false } if ev.Exec.Process == nil { - return zeroValue + return false } return ev.FieldHandlers.ResolveProcessEnvsTruncated(ev, ev.Exec.Process) } // GetExecEuid returns the value of the field, resolving if necessary func (ev *Event) GetExecEuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } return ev.Exec.Process.Credentials.EUID } // GetExecEuser returns the value of the field, resolving if necessary func (ev *Event) GetExecEuser() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.Exec.Process.Credentials.EUser } // GetExecExecTime returns the value of the field, resolving if necessary func (ev *Event) GetExecExecTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "exec" { - return zeroValue + return time.Time{} } if ev.Exec.Process == nil { - return zeroValue + return time.Time{} } return ev.Exec.Process.ExecTime } // GetExecExitTime returns the value of the field, resolving if necessary func (ev *Event) GetExecExitTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "exec" { - return zeroValue + return time.Time{} } if ev.Exec.Process == nil { - return zeroValue + return time.Time{} } return ev.Exec.Process.ExitTime } // GetExecFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetExecFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint64(0) } if ev.Exec.Process == nil { - return zeroValue + return uint64(0) } if !ev.Exec.Process.IsNotKworker() { return uint64(0) @@ -963,12 +867,11 @@ func (ev *Event) GetExecFileChangeTime() uint64 { // GetExecFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetExecFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.IsNotKworker() { return "" @@ -978,12 +881,11 @@ func (ev *Event) GetExecFileFilesystem() string { // GetExecFileGid returns the value of the field, resolving if necessary func (ev *Event) GetExecFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } if !ev.Exec.Process.IsNotKworker() { return uint32(0) @@ -993,12 +895,11 @@ func (ev *Event) GetExecFileGid() uint32 { // GetExecFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetExecFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.IsNotKworker() { return "" @@ -1008,12 +909,11 @@ func (ev *Event) GetExecFileGroup() string { // GetExecFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetExecFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exec" { - return zeroValue + return []string{} } if ev.Exec.Process == nil { - return zeroValue + return []string{} } if !ev.Exec.Process.IsNotKworker() { return []string{} @@ -1023,12 +923,11 @@ func (ev *Event) GetExecFileHashes() []string { // GetExecFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetExecFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "exec" { - return zeroValue + return false } if ev.Exec.Process == nil { - return zeroValue + return false } if !ev.Exec.Process.IsNotKworker() { return false @@ -1038,12 +937,11 @@ func (ev *Event) GetExecFileInUpperLayer() bool { // GetExecFileInode returns the value of the field, resolving if necessary func (ev *Event) GetExecFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint64(0) } if ev.Exec.Process == nil { - return zeroValue + return uint64(0) } if !ev.Exec.Process.IsNotKworker() { return uint64(0) @@ -1053,12 +951,11 @@ func (ev *Event) GetExecFileInode() uint64 { // GetExecFileMode returns the value of the field, resolving if necessary func (ev *Event) GetExecFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint16(0) } if ev.Exec.Process == nil { - return zeroValue + return uint16(0) } if !ev.Exec.Process.IsNotKworker() { return uint16(0) @@ -1068,12 +965,11 @@ func (ev *Event) GetExecFileMode() uint16 { // GetExecFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetExecFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint64(0) } if ev.Exec.Process == nil { - return zeroValue + return uint64(0) } if !ev.Exec.Process.IsNotKworker() { return uint64(0) @@ -1083,12 +979,11 @@ func (ev *Event) GetExecFileModificationTime() uint64 { // GetExecFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetExecFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } if !ev.Exec.Process.IsNotKworker() { return uint32(0) @@ -1098,12 +993,11 @@ func (ev *Event) GetExecFileMountId() uint32 { // GetExecFileName returns the value of the field, resolving if necessary func (ev *Event) GetExecFileName() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.IsNotKworker() { return "" @@ -1113,24 +1007,22 @@ func (ev *Event) GetExecFileName() string { // GetExecFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetExecFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "exec" { - return zeroValue + return 0 } if ev.Exec.Process == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Exec.Process.FileEvent)) } // GetExecFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetExecFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.IsNotKworker() { return "" @@ -1140,12 +1032,11 @@ func (ev *Event) GetExecFilePackageName() string { // GetExecFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetExecFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.IsNotKworker() { return "" @@ -1155,12 +1046,11 @@ func (ev *Event) GetExecFilePackageSourceVersion() string { // GetExecFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetExecFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.IsNotKworker() { return "" @@ -1170,12 +1060,11 @@ func (ev *Event) GetExecFilePackageVersion() string { // GetExecFilePath returns the value of the field, resolving if necessary func (ev *Event) GetExecFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.IsNotKworker() { return "" @@ -1185,24 +1074,22 @@ func (ev *Event) GetExecFilePath() string { // GetExecFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetExecFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "exec" { - return zeroValue + return 0 } if ev.Exec.Process == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Exec.Process.FileEvent)) } // GetExecFileRights returns the value of the field, resolving if necessary func (ev *Event) GetExecFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "exec" { - return zeroValue + return 0 } if ev.Exec.Process == nil { - return zeroValue + return 0 } if !ev.Exec.Process.IsNotKworker() { return 0 @@ -1212,12 +1099,11 @@ func (ev *Event) GetExecFileRights() int { // GetExecFileUid returns the value of the field, resolving if necessary func (ev *Event) GetExecFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } if !ev.Exec.Process.IsNotKworker() { return uint32(0) @@ -1227,12 +1113,11 @@ func (ev *Event) GetExecFileUid() uint32 { // GetExecFileUser returns the value of the field, resolving if necessary func (ev *Event) GetExecFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.IsNotKworker() { return "" @@ -1242,96 +1127,88 @@ func (ev *Event) GetExecFileUser() string { // GetExecForkTime returns the value of the field, resolving if necessary func (ev *Event) GetExecForkTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "exec" { - return zeroValue + return time.Time{} } if ev.Exec.Process == nil { - return zeroValue + return time.Time{} } return ev.Exec.Process.ForkTime } // GetExecFsgid returns the value of the field, resolving if necessary func (ev *Event) GetExecFsgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } return ev.Exec.Process.Credentials.FSGID } // GetExecFsgroup returns the value of the field, resolving if necessary func (ev *Event) GetExecFsgroup() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.Exec.Process.Credentials.FSGroup } // GetExecFsuid returns the value of the field, resolving if necessary func (ev *Event) GetExecFsuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } return ev.Exec.Process.Credentials.FSUID } // GetExecFsuser returns the value of the field, resolving if necessary func (ev *Event) GetExecFsuser() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.Exec.Process.Credentials.FSUser } // GetExecGid returns the value of the field, resolving if necessary func (ev *Event) GetExecGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } return ev.Exec.Process.Credentials.GID } // GetExecGroup returns the value of the field, resolving if necessary func (ev *Event) GetExecGroup() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.Exec.Process.Credentials.Group } // GetExecInterpreterFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint64(0) } if ev.Exec.Process == nil { - return zeroValue + return uint64(0) } if !ev.Exec.Process.HasInterpreter() { return uint64(0) @@ -1341,12 +1218,11 @@ func (ev *Event) GetExecInterpreterFileChangeTime() uint64 { // GetExecInterpreterFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.HasInterpreter() { return "" @@ -1356,12 +1232,11 @@ func (ev *Event) GetExecInterpreterFileFilesystem() string { // GetExecInterpreterFileGid returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } if !ev.Exec.Process.HasInterpreter() { return uint32(0) @@ -1371,12 +1246,11 @@ func (ev *Event) GetExecInterpreterFileGid() uint32 { // GetExecInterpreterFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.HasInterpreter() { return "" @@ -1386,12 +1260,11 @@ func (ev *Event) GetExecInterpreterFileGroup() string { // GetExecInterpreterFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exec" { - return zeroValue + return []string{} } if ev.Exec.Process == nil { - return zeroValue + return []string{} } if !ev.Exec.Process.HasInterpreter() { return []string{} @@ -1401,12 +1274,11 @@ func (ev *Event) GetExecInterpreterFileHashes() []string { // GetExecInterpreterFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "exec" { - return zeroValue + return false } if ev.Exec.Process == nil { - return zeroValue + return false } if !ev.Exec.Process.HasInterpreter() { return false @@ -1416,12 +1288,11 @@ func (ev *Event) GetExecInterpreterFileInUpperLayer() bool { // GetExecInterpreterFileInode returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint64(0) } if ev.Exec.Process == nil { - return zeroValue + return uint64(0) } if !ev.Exec.Process.HasInterpreter() { return uint64(0) @@ -1431,12 +1302,11 @@ func (ev *Event) GetExecInterpreterFileInode() uint64 { // GetExecInterpreterFileMode returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint16(0) } if ev.Exec.Process == nil { - return zeroValue + return uint16(0) } if !ev.Exec.Process.HasInterpreter() { return uint16(0) @@ -1446,12 +1316,11 @@ func (ev *Event) GetExecInterpreterFileMode() uint16 { // GetExecInterpreterFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint64(0) } if ev.Exec.Process == nil { - return zeroValue + return uint64(0) } if !ev.Exec.Process.HasInterpreter() { return uint64(0) @@ -1461,12 +1330,11 @@ func (ev *Event) GetExecInterpreterFileModificationTime() uint64 { // GetExecInterpreterFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } if !ev.Exec.Process.HasInterpreter() { return uint32(0) @@ -1476,12 +1344,11 @@ func (ev *Event) GetExecInterpreterFileMountId() uint32 { // GetExecInterpreterFileName returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileName() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.HasInterpreter() { return "" @@ -1491,24 +1358,22 @@ func (ev *Event) GetExecInterpreterFileName() string { // GetExecInterpreterFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "exec" { - return zeroValue + return 0 } if ev.Exec.Process == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Exec.Process.LinuxBinprm.FileEvent)) } // GetExecInterpreterFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.HasInterpreter() { return "" @@ -1518,12 +1383,11 @@ func (ev *Event) GetExecInterpreterFilePackageName() string { // GetExecInterpreterFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.HasInterpreter() { return "" @@ -1533,12 +1397,11 @@ func (ev *Event) GetExecInterpreterFilePackageSourceVersion() string { // GetExecInterpreterFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.HasInterpreter() { return "" @@ -1548,12 +1411,11 @@ func (ev *Event) GetExecInterpreterFilePackageVersion() string { // GetExecInterpreterFilePath returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.HasInterpreter() { return "" @@ -1563,24 +1425,22 @@ func (ev *Event) GetExecInterpreterFilePath() string { // GetExecInterpreterFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "exec" { - return zeroValue + return 0 } if ev.Exec.Process == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Exec.Process.LinuxBinprm.FileEvent)) } // GetExecInterpreterFileRights returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "exec" { - return zeroValue + return 0 } if ev.Exec.Process == nil { - return zeroValue + return 0 } if !ev.Exec.Process.HasInterpreter() { return 0 @@ -1590,12 +1450,11 @@ func (ev *Event) GetExecInterpreterFileRights() int { // GetExecInterpreterFileUid returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } if !ev.Exec.Process.HasInterpreter() { return uint32(0) @@ -1605,12 +1464,11 @@ func (ev *Event) GetExecInterpreterFileUid() uint32 { // GetExecInterpreterFileUser returns the value of the field, resolving if necessary func (ev *Event) GetExecInterpreterFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } if !ev.Exec.Process.HasInterpreter() { return "" @@ -1620,426 +1478,390 @@ func (ev *Event) GetExecInterpreterFileUser() string { // GetExecIsKworker returns the value of the field, resolving if necessary func (ev *Event) GetExecIsKworker() bool { - zeroValue := false if ev.GetEventType().String() != "exec" { - return zeroValue + return false } if ev.Exec.Process == nil { - return zeroValue + return false } return ev.Exec.Process.PIDContext.IsKworker } // GetExecIsThread returns the value of the field, resolving if necessary func (ev *Event) GetExecIsThread() bool { - zeroValue := false if ev.GetEventType().String() != "exec" { - return zeroValue + return false } if ev.Exec.Process == nil { - return zeroValue + return false } return ev.Exec.Process.IsThread } // GetExecPid returns the value of the field, resolving if necessary func (ev *Event) GetExecPid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } return ev.Exec.Process.PIDContext.Pid } // GetExecPpid returns the value of the field, resolving if necessary func (ev *Event) GetExecPpid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } return ev.Exec.Process.PPid } // GetExecTid returns the value of the field, resolving if necessary func (ev *Event) GetExecTid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } return ev.Exec.Process.PIDContext.Tid } // GetExecTtyName returns the value of the field, resolving if necessary func (ev *Event) GetExecTtyName() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.Exec.Process.TTYName } // GetExecUid returns the value of the field, resolving if necessary func (ev *Event) GetExecUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } return ev.Exec.Process.Credentials.UID } // GetExecUser returns the value of the field, resolving if necessary func (ev *Event) GetExecUser() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.Exec.Process.Credentials.User } // GetExecUserSessionK8sGroups returns the value of the field, resolving if necessary func (ev *Event) GetExecUserSessionK8sGroups() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exec" { - return zeroValue + return []string{} } if ev.Exec.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.Exec.Process.UserSession) } // GetExecUserSessionK8sUid returns the value of the field, resolving if necessary func (ev *Event) GetExecUserSessionK8sUid() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveK8SUID(ev, &ev.Exec.Process.UserSession) } // GetExecUserSessionK8sUsername returns the value of the field, resolving if necessary func (ev *Event) GetExecUserSessionK8sUsername() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveK8SUsername(ev, &ev.Exec.Process.UserSession) } // GetExitArgs returns the value of the field, resolving if necessary func (ev *Event) GetExitArgs() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgs(ev, ev.Exit.Process) } // GetExitArgsFlags returns the value of the field, resolving if necessary func (ev *Event) GetExitArgsFlags() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exit" { - return zeroValue + return []string{} } if ev.Exit.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgsFlags(ev, ev.Exit.Process) } // GetExitArgsOptions returns the value of the field, resolving if necessary func (ev *Event) GetExitArgsOptions() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exit" { - return zeroValue + return []string{} } if ev.Exit.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgsOptions(ev, ev.Exit.Process) } // GetExitArgsScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetExitArgsScrubbed() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, ev.Exit.Process) } // GetExitArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetExitArgsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "exit" { - return zeroValue + return false } if ev.Exit.Process == nil { - return zeroValue + return false } return ev.FieldHandlers.ResolveProcessArgsTruncated(ev, ev.Exit.Process) } // GetExitArgv returns the value of the field, resolving if necessary func (ev *Event) GetExitArgv() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exit" { - return zeroValue + return []string{} } if ev.Exit.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgv(ev, ev.Exit.Process) } // GetExitArgv0 returns the value of the field, resolving if necessary func (ev *Event) GetExitArgv0() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgv0(ev, ev.Exit.Process) } // GetExitArgvScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetExitArgvScrubbed() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exit" { - return zeroValue + return []string{} } if ev.Exit.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, ev.Exit.Process) } // GetExitCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetExitCapEffective() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint64(0) } if ev.Exit.Process == nil { - return zeroValue + return uint64(0) } return ev.Exit.Process.Credentials.CapEffective } // GetExitCapPermitted returns the value of the field, resolving if necessary func (ev *Event) GetExitCapPermitted() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint64(0) } if ev.Exit.Process == nil { - return zeroValue + return uint64(0) } return ev.Exit.Process.Credentials.CapPermitted } // GetExitCause returns the value of the field, resolving if necessary func (ev *Event) GetExitCause() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } return ev.Exit.Cause } // GetExitCode returns the value of the field, resolving if necessary func (ev *Event) GetExitCode() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } return ev.Exit.Code } // GetExitComm returns the value of the field, resolving if necessary func (ev *Event) GetExitComm() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.Exit.Process.Comm } // GetExitContainerId returns the value of the field, resolving if necessary func (ev *Event) GetExitContainerId() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.Exit.Process.ContainerID } // GetExitCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetExitCreatedAt() int { - zeroValue := 0 if ev.GetEventType().String() != "exit" { - return zeroValue + return 0 } if ev.Exit.Process == nil { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveProcessCreatedAt(ev, ev.Exit.Process) } // GetExitEgid returns the value of the field, resolving if necessary func (ev *Event) GetExitEgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } return ev.Exit.Process.Credentials.EGID } // GetExitEgroup returns the value of the field, resolving if necessary func (ev *Event) GetExitEgroup() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.Exit.Process.Credentials.EGroup } // GetExitEnvp returns the value of the field, resolving if necessary func (ev *Event) GetExitEnvp() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exit" { - return zeroValue + return []string{} } if ev.Exit.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exit.Process) } // GetExitEnvs returns the value of the field, resolving if necessary func (ev *Event) GetExitEnvs() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exit" { - return zeroValue + return []string{} } if ev.Exit.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exit.Process) } // GetExitEnvsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetExitEnvsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "exit" { - return zeroValue + return false } if ev.Exit.Process == nil { - return zeroValue + return false } return ev.FieldHandlers.ResolveProcessEnvsTruncated(ev, ev.Exit.Process) } // GetExitEuid returns the value of the field, resolving if necessary func (ev *Event) GetExitEuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } return ev.Exit.Process.Credentials.EUID } // GetExitEuser returns the value of the field, resolving if necessary func (ev *Event) GetExitEuser() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.Exit.Process.Credentials.EUser } // GetExitExecTime returns the value of the field, resolving if necessary func (ev *Event) GetExitExecTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "exit" { - return zeroValue + return time.Time{} } if ev.Exit.Process == nil { - return zeroValue + return time.Time{} } return ev.Exit.Process.ExecTime } // GetExitExitTime returns the value of the field, resolving if necessary func (ev *Event) GetExitExitTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "exit" { - return zeroValue + return time.Time{} } if ev.Exit.Process == nil { - return zeroValue + return time.Time{} } return ev.Exit.Process.ExitTime } // GetExitFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetExitFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint64(0) } if ev.Exit.Process == nil { - return zeroValue + return uint64(0) } if !ev.Exit.Process.IsNotKworker() { return uint64(0) @@ -2049,12 +1871,11 @@ func (ev *Event) GetExitFileChangeTime() uint64 { // GetExitFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetExitFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.IsNotKworker() { return "" @@ -2064,12 +1885,11 @@ func (ev *Event) GetExitFileFilesystem() string { // GetExitFileGid returns the value of the field, resolving if necessary func (ev *Event) GetExitFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } if !ev.Exit.Process.IsNotKworker() { return uint32(0) @@ -2079,12 +1899,11 @@ func (ev *Event) GetExitFileGid() uint32 { // GetExitFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetExitFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.IsNotKworker() { return "" @@ -2094,12 +1913,11 @@ func (ev *Event) GetExitFileGroup() string { // GetExitFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetExitFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exit" { - return zeroValue + return []string{} } if ev.Exit.Process == nil { - return zeroValue + return []string{} } if !ev.Exit.Process.IsNotKworker() { return []string{} @@ -2109,12 +1927,11 @@ func (ev *Event) GetExitFileHashes() []string { // GetExitFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetExitFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "exit" { - return zeroValue + return false } if ev.Exit.Process == nil { - return zeroValue + return false } if !ev.Exit.Process.IsNotKworker() { return false @@ -2124,12 +1941,11 @@ func (ev *Event) GetExitFileInUpperLayer() bool { // GetExitFileInode returns the value of the field, resolving if necessary func (ev *Event) GetExitFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint64(0) } if ev.Exit.Process == nil { - return zeroValue + return uint64(0) } if !ev.Exit.Process.IsNotKworker() { return uint64(0) @@ -2139,12 +1955,11 @@ func (ev *Event) GetExitFileInode() uint64 { // GetExitFileMode returns the value of the field, resolving if necessary func (ev *Event) GetExitFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint16(0) } if ev.Exit.Process == nil { - return zeroValue + return uint16(0) } if !ev.Exit.Process.IsNotKworker() { return uint16(0) @@ -2154,12 +1969,11 @@ func (ev *Event) GetExitFileMode() uint16 { // GetExitFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetExitFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint64(0) } if ev.Exit.Process == nil { - return zeroValue + return uint64(0) } if !ev.Exit.Process.IsNotKworker() { return uint64(0) @@ -2169,12 +1983,11 @@ func (ev *Event) GetExitFileModificationTime() uint64 { // GetExitFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetExitFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } if !ev.Exit.Process.IsNotKworker() { return uint32(0) @@ -2184,12 +1997,11 @@ func (ev *Event) GetExitFileMountId() uint32 { // GetExitFileName returns the value of the field, resolving if necessary func (ev *Event) GetExitFileName() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.IsNotKworker() { return "" @@ -2199,24 +2011,22 @@ func (ev *Event) GetExitFileName() string { // GetExitFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetExitFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "exit" { - return zeroValue + return 0 } if ev.Exit.Process == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Exit.Process.FileEvent)) } // GetExitFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetExitFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.IsNotKworker() { return "" @@ -2226,12 +2036,11 @@ func (ev *Event) GetExitFilePackageName() string { // GetExitFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetExitFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.IsNotKworker() { return "" @@ -2241,12 +2050,11 @@ func (ev *Event) GetExitFilePackageSourceVersion() string { // GetExitFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetExitFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.IsNotKworker() { return "" @@ -2256,12 +2064,11 @@ func (ev *Event) GetExitFilePackageVersion() string { // GetExitFilePath returns the value of the field, resolving if necessary func (ev *Event) GetExitFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.IsNotKworker() { return "" @@ -2271,24 +2078,22 @@ func (ev *Event) GetExitFilePath() string { // GetExitFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetExitFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "exit" { - return zeroValue + return 0 } if ev.Exit.Process == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Exit.Process.FileEvent)) } // GetExitFileRights returns the value of the field, resolving if necessary func (ev *Event) GetExitFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "exit" { - return zeroValue + return 0 } if ev.Exit.Process == nil { - return zeroValue + return 0 } if !ev.Exit.Process.IsNotKworker() { return 0 @@ -2298,12 +2103,11 @@ func (ev *Event) GetExitFileRights() int { // GetExitFileUid returns the value of the field, resolving if necessary func (ev *Event) GetExitFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } if !ev.Exit.Process.IsNotKworker() { return uint32(0) @@ -2313,12 +2117,11 @@ func (ev *Event) GetExitFileUid() uint32 { // GetExitFileUser returns the value of the field, resolving if necessary func (ev *Event) GetExitFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.IsNotKworker() { return "" @@ -2328,96 +2131,88 @@ func (ev *Event) GetExitFileUser() string { // GetExitForkTime returns the value of the field, resolving if necessary func (ev *Event) GetExitForkTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "exit" { - return zeroValue + return time.Time{} } if ev.Exit.Process == nil { - return zeroValue + return time.Time{} } return ev.Exit.Process.ForkTime } // GetExitFsgid returns the value of the field, resolving if necessary func (ev *Event) GetExitFsgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } return ev.Exit.Process.Credentials.FSGID } // GetExitFsgroup returns the value of the field, resolving if necessary func (ev *Event) GetExitFsgroup() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.Exit.Process.Credentials.FSGroup } // GetExitFsuid returns the value of the field, resolving if necessary func (ev *Event) GetExitFsuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } return ev.Exit.Process.Credentials.FSUID } // GetExitFsuser returns the value of the field, resolving if necessary func (ev *Event) GetExitFsuser() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.Exit.Process.Credentials.FSUser } // GetExitGid returns the value of the field, resolving if necessary func (ev *Event) GetExitGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } return ev.Exit.Process.Credentials.GID } // GetExitGroup returns the value of the field, resolving if necessary func (ev *Event) GetExitGroup() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.Exit.Process.Credentials.Group } // GetExitInterpreterFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint64(0) } if ev.Exit.Process == nil { - return zeroValue + return uint64(0) } if !ev.Exit.Process.HasInterpreter() { return uint64(0) @@ -2427,12 +2222,11 @@ func (ev *Event) GetExitInterpreterFileChangeTime() uint64 { // GetExitInterpreterFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.HasInterpreter() { return "" @@ -2442,12 +2236,11 @@ func (ev *Event) GetExitInterpreterFileFilesystem() string { // GetExitInterpreterFileGid returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } if !ev.Exit.Process.HasInterpreter() { return uint32(0) @@ -2457,12 +2250,11 @@ func (ev *Event) GetExitInterpreterFileGid() uint32 { // GetExitInterpreterFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.HasInterpreter() { return "" @@ -2472,12 +2264,11 @@ func (ev *Event) GetExitInterpreterFileGroup() string { // GetExitInterpreterFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exit" { - return zeroValue + return []string{} } if ev.Exit.Process == nil { - return zeroValue + return []string{} } if !ev.Exit.Process.HasInterpreter() { return []string{} @@ -2487,12 +2278,11 @@ func (ev *Event) GetExitInterpreterFileHashes() []string { // GetExitInterpreterFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "exit" { - return zeroValue + return false } if ev.Exit.Process == nil { - return zeroValue + return false } if !ev.Exit.Process.HasInterpreter() { return false @@ -2502,12 +2292,11 @@ func (ev *Event) GetExitInterpreterFileInUpperLayer() bool { // GetExitInterpreterFileInode returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint64(0) } if ev.Exit.Process == nil { - return zeroValue + return uint64(0) } if !ev.Exit.Process.HasInterpreter() { return uint64(0) @@ -2517,12 +2306,11 @@ func (ev *Event) GetExitInterpreterFileInode() uint64 { // GetExitInterpreterFileMode returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint16(0) } if ev.Exit.Process == nil { - return zeroValue + return uint16(0) } if !ev.Exit.Process.HasInterpreter() { return uint16(0) @@ -2532,12 +2320,11 @@ func (ev *Event) GetExitInterpreterFileMode() uint16 { // GetExitInterpreterFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint64(0) } if ev.Exit.Process == nil { - return zeroValue + return uint64(0) } if !ev.Exit.Process.HasInterpreter() { return uint64(0) @@ -2547,12 +2334,11 @@ func (ev *Event) GetExitInterpreterFileModificationTime() uint64 { // GetExitInterpreterFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } if !ev.Exit.Process.HasInterpreter() { return uint32(0) @@ -2562,12 +2348,11 @@ func (ev *Event) GetExitInterpreterFileMountId() uint32 { // GetExitInterpreterFileName returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileName() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.HasInterpreter() { return "" @@ -2577,24 +2362,22 @@ func (ev *Event) GetExitInterpreterFileName() string { // GetExitInterpreterFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "exit" { - return zeroValue + return 0 } if ev.Exit.Process == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Exit.Process.LinuxBinprm.FileEvent)) } // GetExitInterpreterFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.HasInterpreter() { return "" @@ -2604,12 +2387,11 @@ func (ev *Event) GetExitInterpreterFilePackageName() string { // GetExitInterpreterFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.HasInterpreter() { return "" @@ -2619,12 +2401,11 @@ func (ev *Event) GetExitInterpreterFilePackageSourceVersion() string { // GetExitInterpreterFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.HasInterpreter() { return "" @@ -2634,12 +2415,11 @@ func (ev *Event) GetExitInterpreterFilePackageVersion() string { // GetExitInterpreterFilePath returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.HasInterpreter() { return "" @@ -2649,24 +2429,22 @@ func (ev *Event) GetExitInterpreterFilePath() string { // GetExitInterpreterFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "exit" { - return zeroValue + return 0 } if ev.Exit.Process == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Exit.Process.LinuxBinprm.FileEvent)) } // GetExitInterpreterFileRights returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "exit" { - return zeroValue + return 0 } if ev.Exit.Process == nil { - return zeroValue + return 0 } if !ev.Exit.Process.HasInterpreter() { return 0 @@ -2676,12 +2454,11 @@ func (ev *Event) GetExitInterpreterFileRights() int { // GetExitInterpreterFileUid returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } if !ev.Exit.Process.HasInterpreter() { return uint32(0) @@ -2691,12 +2468,11 @@ func (ev *Event) GetExitInterpreterFileUid() uint32 { // GetExitInterpreterFileUser returns the value of the field, resolving if necessary func (ev *Event) GetExitInterpreterFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } if !ev.Exit.Process.HasInterpreter() { return "" @@ -2706,1521 +2482,1356 @@ func (ev *Event) GetExitInterpreterFileUser() string { // GetExitIsKworker returns the value of the field, resolving if necessary func (ev *Event) GetExitIsKworker() bool { - zeroValue := false if ev.GetEventType().String() != "exit" { - return zeroValue + return false } if ev.Exit.Process == nil { - return zeroValue + return false } return ev.Exit.Process.PIDContext.IsKworker } // GetExitIsThread returns the value of the field, resolving if necessary func (ev *Event) GetExitIsThread() bool { - zeroValue := false if ev.GetEventType().String() != "exit" { - return zeroValue + return false } if ev.Exit.Process == nil { - return zeroValue + return false } return ev.Exit.Process.IsThread } // GetExitPid returns the value of the field, resolving if necessary func (ev *Event) GetExitPid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } return ev.Exit.Process.PIDContext.Pid } // GetExitPpid returns the value of the field, resolving if necessary func (ev *Event) GetExitPpid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } return ev.Exit.Process.PPid } // GetExitTid returns the value of the field, resolving if necessary func (ev *Event) GetExitTid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } return ev.Exit.Process.PIDContext.Tid } // GetExitTtyName returns the value of the field, resolving if necessary func (ev *Event) GetExitTtyName() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.Exit.Process.TTYName } // GetExitUid returns the value of the field, resolving if necessary func (ev *Event) GetExitUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } return ev.Exit.Process.Credentials.UID } // GetExitUser returns the value of the field, resolving if necessary func (ev *Event) GetExitUser() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.Exit.Process.Credentials.User } // GetExitUserSessionK8sGroups returns the value of the field, resolving if necessary func (ev *Event) GetExitUserSessionK8sGroups() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exit" { - return zeroValue + return []string{} } if ev.Exit.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.Exit.Process.UserSession) } // GetExitUserSessionK8sUid returns the value of the field, resolving if necessary func (ev *Event) GetExitUserSessionK8sUid() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveK8SUID(ev, &ev.Exit.Process.UserSession) } // GetExitUserSessionK8sUsername returns the value of the field, resolving if necessary func (ev *Event) GetExitUserSessionK8sUsername() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveK8SUsername(ev, &ev.Exit.Process.UserSession) } // GetLinkFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint64(0) } return ev.Link.Source.FileFields.CTime } // GetLinkFileDestinationChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint64(0) } return ev.Link.Target.FileFields.CTime } // GetLinkFileDestinationFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.Link.Target) } // GetLinkFileDestinationGid returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint32(0) } return ev.Link.Target.FileFields.GID } // GetLinkFileDestinationGroup returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationGroup() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Link.Target.FileFields) } // GetLinkFileDestinationHashes returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "link" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Link.Target) } // GetLinkFileDestinationInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "link" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.Link.Target.FileFields) } // GetLinkFileDestinationInode returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint64(0) } return ev.Link.Target.FileFields.PathKey.Inode } // GetLinkFileDestinationMode returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint16(0) } return ev.Link.Target.FileFields.Mode } // GetLinkFileDestinationModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint64(0) } return ev.Link.Target.FileFields.MTime } // GetLinkFileDestinationMountId returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint32(0) } return ev.Link.Target.FileFields.PathKey.MountID } // GetLinkFileDestinationName returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationName() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Link.Target) } // GetLinkFileDestinationNameLength returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "link" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Link.Target)) } // GetLinkFileDestinationPackageName returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationPackageName() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.Link.Target) } // GetLinkFileDestinationPackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationPackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.Link.Target) } // GetLinkFileDestinationPackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationPackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.Link.Target) } // GetLinkFileDestinationPath returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationPath() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Link.Target) } // GetLinkFileDestinationPathLength returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationPathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "link" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Link.Target)) } // GetLinkFileDestinationRights returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationRights() int { - zeroValue := 0 if ev.GetEventType().String() != "link" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.Link.Target.FileFields) } // GetLinkFileDestinationUid returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint32(0) } return ev.Link.Target.FileFields.UID } // GetLinkFileDestinationUser returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileDestinationUser() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Link.Target.FileFields) } // GetLinkFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.Link.Source) } // GetLinkFileGid returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint32(0) } return ev.Link.Source.FileFields.GID } // GetLinkFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Link.Source.FileFields) } // GetLinkFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "link" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Link.Source) } // GetLinkFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "link" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.Link.Source.FileFields) } // GetLinkFileInode returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint64(0) } return ev.Link.Source.FileFields.PathKey.Inode } // GetLinkFileMode returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint16(0) } return ev.Link.Source.FileFields.Mode } // GetLinkFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint64(0) } return ev.Link.Source.FileFields.MTime } // GetLinkFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint32(0) } return ev.Link.Source.FileFields.PathKey.MountID } // GetLinkFileName returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileName() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Link.Source) } // GetLinkFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "link" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Link.Source)) } // GetLinkFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetLinkFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.Link.Source) } // GetLinkFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetLinkFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.Link.Source) } // GetLinkFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetLinkFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.Link.Source) } // GetLinkFilePath returns the value of the field, resolving if necessary func (ev *Event) GetLinkFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Link.Source) } // GetLinkFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetLinkFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "link" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Link.Source)) } // GetLinkFileRights returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "link" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.Link.Source.FileFields) } // GetLinkFileUid returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "link" { - return zeroValue + return uint32(0) } return ev.Link.Source.FileFields.UID } // GetLinkFileUser returns the value of the field, resolving if necessary func (ev *Event) GetLinkFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "link" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Link.Source.FileFields) } // GetLinkRetval returns the value of the field, resolving if necessary func (ev *Event) GetLinkRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "link" { - return zeroValue + return int64(0) } return ev.Link.SyscallEvent.Retval } // GetLoadModuleArgs returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleArgs() string { - zeroValue := "" if ev.GetEventType().String() != "load_module" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveModuleArgs(ev, &ev.LoadModule) } // GetLoadModuleArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleArgsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "load_module" { - return zeroValue + return false } return ev.LoadModule.ArgsTruncated } // GetLoadModuleArgv returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleArgv() []string { - zeroValue := []string{} if ev.GetEventType().String() != "load_module" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveModuleArgv(ev, &ev.LoadModule) } // GetLoadModuleFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "load_module" { - return zeroValue + return uint64(0) } return ev.LoadModule.File.FileFields.CTime } // GetLoadModuleFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "load_module" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.LoadModule.File) } // GetLoadModuleFileGid returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "load_module" { - return zeroValue + return uint32(0) } return ev.LoadModule.File.FileFields.GID } // GetLoadModuleFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "load_module" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.LoadModule.File.FileFields) } // GetLoadModuleFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "load_module" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.LoadModule.File) } // GetLoadModuleFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "load_module" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.LoadModule.File.FileFields) } // GetLoadModuleFileInode returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "load_module" { - return zeroValue + return uint64(0) } return ev.LoadModule.File.FileFields.PathKey.Inode } // GetLoadModuleFileMode returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "load_module" { - return zeroValue + return uint16(0) } return ev.LoadModule.File.FileFields.Mode } // GetLoadModuleFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "load_module" { - return zeroValue + return uint64(0) } return ev.LoadModule.File.FileFields.MTime } // GetLoadModuleFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "load_module" { - return zeroValue + return uint32(0) } return ev.LoadModule.File.FileFields.PathKey.MountID } // GetLoadModuleFileName returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileName() string { - zeroValue := "" if ev.GetEventType().String() != "load_module" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.LoadModule.File) } // GetLoadModuleFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "load_module" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.LoadModule.File)) } // GetLoadModuleFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "load_module" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.LoadModule.File) } // GetLoadModuleFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "load_module" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.LoadModule.File) } // GetLoadModuleFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "load_module" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.LoadModule.File) } // GetLoadModuleFilePath returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "load_module" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.LoadModule.File) } // GetLoadModuleFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "load_module" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.LoadModule.File)) } // GetLoadModuleFileRights returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "load_module" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.LoadModule.File.FileFields) } // GetLoadModuleFileUid returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "load_module" { - return zeroValue + return uint32(0) } return ev.LoadModule.File.FileFields.UID } // GetLoadModuleFileUser returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "load_module" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.LoadModule.File.FileFields) } // GetLoadModuleLoadedFromMemory returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleLoadedFromMemory() bool { - zeroValue := false if ev.GetEventType().String() != "load_module" { - return zeroValue + return false } return ev.LoadModule.LoadedFromMemory } // GetLoadModuleName returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleName() string { - zeroValue := "" if ev.GetEventType().String() != "load_module" { - return zeroValue + return "" } return ev.LoadModule.Name } // GetLoadModuleRetval returns the value of the field, resolving if necessary func (ev *Event) GetLoadModuleRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "load_module" { - return zeroValue + return int64(0) } return ev.LoadModule.SyscallEvent.Retval } // GetMkdirFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "mkdir" { - return zeroValue + return uint64(0) } return ev.Mkdir.File.FileFields.CTime } // GetMkdirFileDestinationMode returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileDestinationMode() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "mkdir" { - return zeroValue + return uint32(0) } return ev.Mkdir.Mode } // GetMkdirFileDestinationRights returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileDestinationRights() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "mkdir" { - return zeroValue + return uint32(0) } return ev.Mkdir.Mode } // GetMkdirFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "mkdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.Mkdir.File) } // GetMkdirFileGid returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "mkdir" { - return zeroValue + return uint32(0) } return ev.Mkdir.File.FileFields.GID } // GetMkdirFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "mkdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Mkdir.File.FileFields) } // GetMkdirFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "mkdir" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Mkdir.File) } // GetMkdirFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "mkdir" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.Mkdir.File.FileFields) } // GetMkdirFileInode returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "mkdir" { - return zeroValue + return uint64(0) } return ev.Mkdir.File.FileFields.PathKey.Inode } // GetMkdirFileMode returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "mkdir" { - return zeroValue + return uint16(0) } return ev.Mkdir.File.FileFields.Mode } // GetMkdirFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "mkdir" { - return zeroValue + return uint64(0) } return ev.Mkdir.File.FileFields.MTime } // GetMkdirFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "mkdir" { - return zeroValue + return uint32(0) } return ev.Mkdir.File.FileFields.PathKey.MountID } // GetMkdirFileName returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileName() string { - zeroValue := "" if ev.GetEventType().String() != "mkdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Mkdir.File) } // GetMkdirFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "mkdir" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Mkdir.File)) } // GetMkdirFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "mkdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.Mkdir.File) } // GetMkdirFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "mkdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.Mkdir.File) } // GetMkdirFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "mkdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.Mkdir.File) } // GetMkdirFilePath returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "mkdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Mkdir.File) } // GetMkdirFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "mkdir" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Mkdir.File)) } // GetMkdirFileRights returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "mkdir" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.Mkdir.File.FileFields) } // GetMkdirFileUid returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "mkdir" { - return zeroValue + return uint32(0) } return ev.Mkdir.File.FileFields.UID } // GetMkdirFileUser returns the value of the field, resolving if necessary func (ev *Event) GetMkdirFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "mkdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Mkdir.File.FileFields) } // GetMkdirRetval returns the value of the field, resolving if necessary func (ev *Event) GetMkdirRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "mkdir" { - return zeroValue + return int64(0) } return ev.Mkdir.SyscallEvent.Retval } // GetMmapFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "mmap" { - return zeroValue + return uint64(0) } return ev.MMap.File.FileFields.CTime } // GetMmapFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "mmap" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.MMap.File) } // GetMmapFileGid returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "mmap" { - return zeroValue + return uint32(0) } return ev.MMap.File.FileFields.GID } // GetMmapFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "mmap" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.MMap.File.FileFields) } // GetMmapFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "mmap" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.MMap.File) } // GetMmapFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "mmap" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.MMap.File.FileFields) } // GetMmapFileInode returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "mmap" { - return zeroValue + return uint64(0) } return ev.MMap.File.FileFields.PathKey.Inode } // GetMmapFileMode returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "mmap" { - return zeroValue + return uint16(0) } return ev.MMap.File.FileFields.Mode } // GetMmapFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "mmap" { - return zeroValue + return uint64(0) } return ev.MMap.File.FileFields.MTime } // GetMmapFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "mmap" { - return zeroValue + return uint32(0) } return ev.MMap.File.FileFields.PathKey.MountID } // GetMmapFileName returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileName() string { - zeroValue := "" if ev.GetEventType().String() != "mmap" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.MMap.File) } // GetMmapFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "mmap" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.MMap.File)) } // GetMmapFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetMmapFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "mmap" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.MMap.File) } // GetMmapFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetMmapFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "mmap" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.MMap.File) } // GetMmapFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetMmapFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "mmap" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.MMap.File) } // GetMmapFilePath returns the value of the field, resolving if necessary func (ev *Event) GetMmapFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "mmap" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.MMap.File) } // GetMmapFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetMmapFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "mmap" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.MMap.File)) } // GetMmapFileRights returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "mmap" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.MMap.File.FileFields) } // GetMmapFileUid returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "mmap" { - return zeroValue + return uint32(0) } return ev.MMap.File.FileFields.UID } // GetMmapFileUser returns the value of the field, resolving if necessary func (ev *Event) GetMmapFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "mmap" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.MMap.File.FileFields) } // GetMmapFlags returns the value of the field, resolving if necessary func (ev *Event) GetMmapFlags() int { - zeroValue := 0 if ev.GetEventType().String() != "mmap" { - return zeroValue + return 0 } return ev.MMap.Flags } // GetMmapProtection returns the value of the field, resolving if necessary func (ev *Event) GetMmapProtection() int { - zeroValue := 0 if ev.GetEventType().String() != "mmap" { - return zeroValue + return 0 } return ev.MMap.Protection } // GetMmapRetval returns the value of the field, resolving if necessary func (ev *Event) GetMmapRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "mmap" { - return zeroValue + return int64(0) } return ev.MMap.SyscallEvent.Retval } // GetMountFsType returns the value of the field, resolving if necessary func (ev *Event) GetMountFsType() string { - zeroValue := "" if ev.GetEventType().String() != "mount" { - return zeroValue + return "" } return ev.Mount.Mount.FSType } // GetMountMountpointPath returns the value of the field, resolving if necessary func (ev *Event) GetMountMountpointPath() string { - zeroValue := "" if ev.GetEventType().String() != "mount" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveMountPointPath(ev, &ev.Mount) } // GetMountRetval returns the value of the field, resolving if necessary func (ev *Event) GetMountRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "mount" { - return zeroValue + return int64(0) } return ev.Mount.SyscallEvent.Retval } // GetMountRootPath returns the value of the field, resolving if necessary func (ev *Event) GetMountRootPath() string { - zeroValue := "" if ev.GetEventType().String() != "mount" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveMountRootPath(ev, &ev.Mount) } // GetMountSourcePath returns the value of the field, resolving if necessary func (ev *Event) GetMountSourcePath() string { - zeroValue := "" if ev.GetEventType().String() != "mount" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveMountSourcePath(ev, &ev.Mount) } // GetMprotectReqProtection returns the value of the field, resolving if necessary func (ev *Event) GetMprotectReqProtection() int { - zeroValue := 0 if ev.GetEventType().String() != "mprotect" { - return zeroValue + return 0 } return ev.MProtect.ReqProtection } // GetMprotectRetval returns the value of the field, resolving if necessary func (ev *Event) GetMprotectRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "mprotect" { - return zeroValue + return int64(0) } return ev.MProtect.SyscallEvent.Retval } // GetMprotectVmProtection returns the value of the field, resolving if necessary func (ev *Event) GetMprotectVmProtection() int { - zeroValue := 0 if ev.GetEventType().String() != "mprotect" { - return zeroValue + return 0 } return ev.MProtect.VMProtection } // GetNetworkDestinationIp returns the value of the field, resolving if necessary func (ev *Event) GetNetworkDestinationIp() net.IPNet { - zeroValue := net.IPNet{} if ev.GetEventType().String() != "dns" { - return zeroValue + return net.IPNet{} } return ev.NetworkContext.Destination.IPNet } // GetNetworkDestinationPort returns the value of the field, resolving if necessary func (ev *Event) GetNetworkDestinationPort() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "dns" { - return zeroValue + return uint16(0) } return ev.NetworkContext.Destination.Port } // GetNetworkDeviceIfindex returns the value of the field, resolving if necessary func (ev *Event) GetNetworkDeviceIfindex() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "dns" { - return zeroValue + return uint32(0) } return ev.NetworkContext.Device.IfIndex } // GetNetworkDeviceIfname returns the value of the field, resolving if necessary func (ev *Event) GetNetworkDeviceIfname() string { - zeroValue := "" if ev.GetEventType().String() != "dns" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveNetworkDeviceIfName(ev, &ev.NetworkContext.Device) } // GetNetworkL3Protocol returns the value of the field, resolving if necessary func (ev *Event) GetNetworkL3Protocol() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "dns" { - return zeroValue + return uint16(0) } return ev.NetworkContext.L3Protocol } // GetNetworkL4Protocol returns the value of the field, resolving if necessary func (ev *Event) GetNetworkL4Protocol() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "dns" { - return zeroValue + return uint16(0) } return ev.NetworkContext.L4Protocol } // GetNetworkSize returns the value of the field, resolving if necessary func (ev *Event) GetNetworkSize() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "dns" { - return zeroValue + return uint32(0) } return ev.NetworkContext.Size } // GetNetworkSourceIp returns the value of the field, resolving if necessary func (ev *Event) GetNetworkSourceIp() net.IPNet { - zeroValue := net.IPNet{} if ev.GetEventType().String() != "dns" { - return zeroValue + return net.IPNet{} } return ev.NetworkContext.Source.IPNet } // GetNetworkSourcePort returns the value of the field, resolving if necessary func (ev *Event) GetNetworkSourcePort() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "dns" { - return zeroValue + return uint16(0) } return ev.NetworkContext.Source.Port } // GetOpenFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "open" { - return zeroValue + return uint64(0) } return ev.Open.File.FileFields.CTime } // GetOpenFileDestinationMode returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileDestinationMode() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "open" { - return zeroValue + return uint32(0) } return ev.Open.Mode } // GetOpenFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "open" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.Open.File) } // GetOpenFileGid returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "open" { - return zeroValue + return uint32(0) } return ev.Open.File.FileFields.GID } // GetOpenFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "open" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Open.File.FileFields) } // GetOpenFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "open" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Open.File) } // GetOpenFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "open" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.Open.File.FileFields) } // GetOpenFileInode returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "open" { - return zeroValue + return uint64(0) } return ev.Open.File.FileFields.PathKey.Inode } // GetOpenFileMode returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "open" { - return zeroValue + return uint16(0) } return ev.Open.File.FileFields.Mode } // GetOpenFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "open" { - return zeroValue + return uint64(0) } return ev.Open.File.FileFields.MTime } // GetOpenFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "open" { - return zeroValue + return uint32(0) } return ev.Open.File.FileFields.PathKey.MountID } // GetOpenFileName returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileName() string { - zeroValue := "" if ev.GetEventType().String() != "open" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Open.File) } // GetOpenFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "open" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Open.File)) } // GetOpenFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetOpenFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "open" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.Open.File) } // GetOpenFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetOpenFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "open" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.Open.File) } // GetOpenFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetOpenFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "open" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.Open.File) } // GetOpenFilePath returns the value of the field, resolving if necessary func (ev *Event) GetOpenFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "open" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Open.File) } // GetOpenFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetOpenFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "open" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Open.File)) } // GetOpenFileRights returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "open" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.Open.File.FileFields) } // GetOpenFileUid returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "open" { - return zeroValue + return uint32(0) } return ev.Open.File.FileFields.UID } // GetOpenFileUser returns the value of the field, resolving if necessary func (ev *Event) GetOpenFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "open" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Open.File.FileFields) } // GetOpenFlags returns the value of the field, resolving if necessary func (ev *Event) GetOpenFlags() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "open" { - return zeroValue + return uint32(0) } return ev.Open.Flags } // GetOpenRetval returns the value of the field, resolving if necessary func (ev *Event) GetOpenRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "open" { - return zeroValue + return int64(0) } return ev.Open.SyscallEvent.Retval } // GetProcessAncestorsArgs returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsArgs() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4237,12 +3848,11 @@ func (ev *Event) GetProcessAncestorsArgs() []string { // GetProcessAncestorsArgsFlags returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsArgsFlags() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4259,12 +3869,11 @@ func (ev *Event) GetProcessAncestorsArgsFlags() []string { // GetProcessAncestorsArgsOptions returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsArgsOptions() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4281,12 +3890,11 @@ func (ev *Event) GetProcessAncestorsArgsOptions() []string { // GetProcessAncestorsArgsScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsArgsScrubbed() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4303,12 +3911,11 @@ func (ev *Event) GetProcessAncestorsArgsScrubbed() []string { // GetProcessAncestorsArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsArgsTruncated() []bool { - zeroValue := []bool{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []bool{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -4325,12 +3932,11 @@ func (ev *Event) GetProcessAncestorsArgsTruncated() []bool { // GetProcessAncestorsArgv returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsArgv() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4347,12 +3953,11 @@ func (ev *Event) GetProcessAncestorsArgv() []string { // GetProcessAncestorsArgv0 returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsArgv0() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4369,12 +3974,11 @@ func (ev *Event) GetProcessAncestorsArgv0() []string { // GetProcessAncestorsArgvScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsArgvScrubbed() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4391,12 +3995,11 @@ func (ev *Event) GetProcessAncestorsArgvScrubbed() []string { // GetProcessAncestorsCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsCapEffective() []uint64 { - zeroValue := []uint64{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint64{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -4413,12 +4016,11 @@ func (ev *Event) GetProcessAncestorsCapEffective() []uint64 { // GetProcessAncestorsCapPermitted returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsCapPermitted() []uint64 { - zeroValue := []uint64{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint64{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -4435,12 +4037,11 @@ func (ev *Event) GetProcessAncestorsCapPermitted() []uint64 { // GetProcessAncestorsComm returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsComm() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4457,12 +4058,11 @@ func (ev *Event) GetProcessAncestorsComm() []string { // GetProcessAncestorsContainerId returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsContainerId() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4479,12 +4079,11 @@ func (ev *Event) GetProcessAncestorsContainerId() []string { // GetProcessAncestorsCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsCreatedAt() []int { - zeroValue := []int{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []int{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -4501,12 +4100,11 @@ func (ev *Event) GetProcessAncestorsCreatedAt() []int { // GetProcessAncestorsEgid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsEgid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -4523,12 +4121,11 @@ func (ev *Event) GetProcessAncestorsEgid() []uint32 { // GetProcessAncestorsEgroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsEgroup() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4545,12 +4142,11 @@ func (ev *Event) GetProcessAncestorsEgroup() []string { // GetProcessAncestorsEnvp returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsEnvp() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4567,12 +4163,11 @@ func (ev *Event) GetProcessAncestorsEnvp() []string { // GetProcessAncestorsEnvs returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsEnvs() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4589,12 +4184,11 @@ func (ev *Event) GetProcessAncestorsEnvs() []string { // GetProcessAncestorsEnvsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsEnvsTruncated() []bool { - zeroValue := []bool{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []bool{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -4611,12 +4205,11 @@ func (ev *Event) GetProcessAncestorsEnvsTruncated() []bool { // GetProcessAncestorsEuid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsEuid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -4633,12 +4226,11 @@ func (ev *Event) GetProcessAncestorsEuid() []uint32 { // GetProcessAncestorsEuser returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsEuser() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4655,12 +4247,11 @@ func (ev *Event) GetProcessAncestorsEuser() []string { // GetProcessAncestorsFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileChangeTime() []uint64 { - zeroValue := []uint64{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint64{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -4677,12 +4268,11 @@ func (ev *Event) GetProcessAncestorsFileChangeTime() []uint64 { // GetProcessAncestorsFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileFilesystem() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4699,12 +4289,11 @@ func (ev *Event) GetProcessAncestorsFileFilesystem() []string { // GetProcessAncestorsFileGid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileGid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -4721,12 +4310,11 @@ func (ev *Event) GetProcessAncestorsFileGid() []uint32 { // GetProcessAncestorsFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileGroup() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4743,12 +4331,11 @@ func (ev *Event) GetProcessAncestorsFileGroup() []string { // GetProcessAncestorsFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileHashes() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4765,12 +4352,11 @@ func (ev *Event) GetProcessAncestorsFileHashes() []string { // GetProcessAncestorsFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileInUpperLayer() []bool { - zeroValue := []bool{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []bool{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -4787,12 +4373,11 @@ func (ev *Event) GetProcessAncestorsFileInUpperLayer() []bool { // GetProcessAncestorsFileInode returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileInode() []uint64 { - zeroValue := []uint64{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint64{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -4809,12 +4394,11 @@ func (ev *Event) GetProcessAncestorsFileInode() []uint64 { // GetProcessAncestorsFileMode returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileMode() []uint16 { - zeroValue := []uint16{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint16{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint16{} } var values []uint16 ctx := eval.NewContext(ev) @@ -4831,12 +4415,11 @@ func (ev *Event) GetProcessAncestorsFileMode() []uint16 { // GetProcessAncestorsFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileModificationTime() []uint64 { - zeroValue := []uint64{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint64{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -4853,12 +4436,11 @@ func (ev *Event) GetProcessAncestorsFileModificationTime() []uint64 { // GetProcessAncestorsFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileMountId() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -4875,12 +4457,11 @@ func (ev *Event) GetProcessAncestorsFileMountId() []uint32 { // GetProcessAncestorsFileName returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileName() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4897,12 +4478,11 @@ func (ev *Event) GetProcessAncestorsFileName() []string { // GetProcessAncestorsFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileNameLength() []int { - zeroValue := []int{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []int{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -4919,12 +4499,11 @@ func (ev *Event) GetProcessAncestorsFileNameLength() []int { // GetProcessAncestorsFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFilePackageName() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4941,12 +4520,11 @@ func (ev *Event) GetProcessAncestorsFilePackageName() []string { // GetProcessAncestorsFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFilePackageSourceVersion() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4963,12 +4541,11 @@ func (ev *Event) GetProcessAncestorsFilePackageSourceVersion() []string { // GetProcessAncestorsFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFilePackageVersion() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -4985,12 +4562,11 @@ func (ev *Event) GetProcessAncestorsFilePackageVersion() []string { // GetProcessAncestorsFilePath returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFilePath() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5007,12 +4583,11 @@ func (ev *Event) GetProcessAncestorsFilePath() []string { // GetProcessAncestorsFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFilePathLength() []int { - zeroValue := []int{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []int{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -5029,12 +4604,11 @@ func (ev *Event) GetProcessAncestorsFilePathLength() []int { // GetProcessAncestorsFileRights returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileRights() []int { - zeroValue := []int{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []int{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -5051,12 +4625,11 @@ func (ev *Event) GetProcessAncestorsFileRights() []int { // GetProcessAncestorsFileUid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileUid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -5073,12 +4646,11 @@ func (ev *Event) GetProcessAncestorsFileUid() []uint32 { // GetProcessAncestorsFileUser returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileUser() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5095,12 +4667,11 @@ func (ev *Event) GetProcessAncestorsFileUser() []string { // GetProcessAncestorsFsgid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFsgid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -5117,12 +4688,11 @@ func (ev *Event) GetProcessAncestorsFsgid() []uint32 { // GetProcessAncestorsFsgroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFsgroup() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5139,12 +4709,11 @@ func (ev *Event) GetProcessAncestorsFsgroup() []string { // GetProcessAncestorsFsuid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFsuid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -5161,12 +4730,11 @@ func (ev *Event) GetProcessAncestorsFsuid() []uint32 { // GetProcessAncestorsFsuser returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFsuser() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5183,12 +4751,11 @@ func (ev *Event) GetProcessAncestorsFsuser() []string { // GetProcessAncestorsGid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsGid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -5205,12 +4772,11 @@ func (ev *Event) GetProcessAncestorsGid() []uint32 { // GetProcessAncestorsGroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsGroup() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5227,12 +4793,11 @@ func (ev *Event) GetProcessAncestorsGroup() []string { // GetProcessAncestorsInterpreterFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileChangeTime() []uint64 { - zeroValue := []uint64{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint64{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -5249,12 +4814,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileChangeTime() []uint64 { // GetProcessAncestorsInterpreterFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileFilesystem() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5271,12 +4835,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileFilesystem() []string { // GetProcessAncestorsInterpreterFileGid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileGid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -5293,12 +4856,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileGid() []uint32 { // GetProcessAncestorsInterpreterFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileGroup() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5315,12 +4877,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileGroup() []string { // GetProcessAncestorsInterpreterFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileHashes() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5337,12 +4898,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileHashes() []string { // GetProcessAncestorsInterpreterFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileInUpperLayer() []bool { - zeroValue := []bool{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []bool{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -5359,12 +4919,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileInUpperLayer() []bool { // GetProcessAncestorsInterpreterFileInode returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileInode() []uint64 { - zeroValue := []uint64{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint64{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -5381,12 +4940,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileInode() []uint64 { // GetProcessAncestorsInterpreterFileMode returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileMode() []uint16 { - zeroValue := []uint16{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint16{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint16{} } var values []uint16 ctx := eval.NewContext(ev) @@ -5403,12 +4961,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileMode() []uint16 { // GetProcessAncestorsInterpreterFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileModificationTime() []uint64 { - zeroValue := []uint64{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint64{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -5425,12 +4982,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileModificationTime() []uint64 { // GetProcessAncestorsInterpreterFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileMountId() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -5447,12 +5003,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileMountId() []uint32 { // GetProcessAncestorsInterpreterFileName returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileName() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5469,12 +5024,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileName() []string { // GetProcessAncestorsInterpreterFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileNameLength() []int { - zeroValue := []int{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []int{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -5491,12 +5045,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileNameLength() []int { // GetProcessAncestorsInterpreterFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFilePackageName() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5513,12 +5066,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFilePackageName() []string { // GetProcessAncestorsInterpreterFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFilePackageSourceVersion() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5535,12 +5087,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFilePackageSourceVersion() []stri // GetProcessAncestorsInterpreterFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFilePackageVersion() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5557,12 +5108,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFilePackageVersion() []string { // GetProcessAncestorsInterpreterFilePath returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFilePath() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5579,12 +5129,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFilePath() []string { // GetProcessAncestorsInterpreterFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFilePathLength() []int { - zeroValue := []int{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []int{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -5601,12 +5150,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFilePathLength() []int { // GetProcessAncestorsInterpreterFileRights returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileRights() []int { - zeroValue := []int{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []int{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -5623,12 +5171,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileRights() []int { // GetProcessAncestorsInterpreterFileUid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileUid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -5645,12 +5192,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileUid() []uint32 { // GetProcessAncestorsInterpreterFileUser returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsInterpreterFileUser() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5667,12 +5213,11 @@ func (ev *Event) GetProcessAncestorsInterpreterFileUser() []string { // GetProcessAncestorsIsKworker returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsIsKworker() []bool { - zeroValue := []bool{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []bool{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -5689,12 +5234,11 @@ func (ev *Event) GetProcessAncestorsIsKworker() []bool { // GetProcessAncestorsIsThread returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsIsThread() []bool { - zeroValue := []bool{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []bool{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -5711,12 +5255,11 @@ func (ev *Event) GetProcessAncestorsIsThread() []bool { // GetProcessAncestorsPid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsPid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -5733,12 +5276,11 @@ func (ev *Event) GetProcessAncestorsPid() []uint32 { // GetProcessAncestorsPpid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsPpid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -5755,12 +5297,11 @@ func (ev *Event) GetProcessAncestorsPpid() []uint32 { // GetProcessAncestorsTid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsTid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -5777,12 +5318,11 @@ func (ev *Event) GetProcessAncestorsTid() []uint32 { // GetProcessAncestorsTtyName returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsTtyName() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5799,12 +5339,11 @@ func (ev *Event) GetProcessAncestorsTtyName() []string { // GetProcessAncestorsUid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsUid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -5821,12 +5360,11 @@ func (ev *Event) GetProcessAncestorsUid() []uint32 { // GetProcessAncestorsUser returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsUser() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5843,12 +5381,11 @@ func (ev *Event) GetProcessAncestorsUser() []string { // GetProcessAncestorsUserSessionK8sGroups returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsUserSessionK8sGroups() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5865,12 +5402,11 @@ func (ev *Event) GetProcessAncestorsUserSessionK8sGroups() []string { // GetProcessAncestorsUserSessionK8sUid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsUserSessionK8sUid() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5887,12 +5423,11 @@ func (ev *Event) GetProcessAncestorsUserSessionK8sUid() []string { // GetProcessAncestorsUserSessionK8sUsername returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsUserSessionK8sUsername() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -5909,207 +5444,184 @@ func (ev *Event) GetProcessAncestorsUserSessionK8sUsername() []string { // GetProcessArgs returns the value of the field, resolving if necessary func (ev *Event) GetProcessArgs() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgs(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessArgsFlags returns the value of the field, resolving if necessary func (ev *Event) GetProcessArgsFlags() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgsFlags(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessArgsOptions returns the value of the field, resolving if necessary func (ev *Event) GetProcessArgsOptions() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgsOptions(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessArgsScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetProcessArgsScrubbed() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetProcessArgsTruncated() bool { - zeroValue := false if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return false } return ev.FieldHandlers.ResolveProcessArgsTruncated(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessArgv returns the value of the field, resolving if necessary func (ev *Event) GetProcessArgv() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgv(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessArgv0 returns the value of the field, resolving if necessary func (ev *Event) GetProcessArgv0() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgv0(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessArgvScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetProcessArgvScrubbed() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetProcessCapEffective() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } return ev.BaseEvent.ProcessContext.Process.Credentials.CapEffective } // GetProcessCapPermitted returns the value of the field, resolving if necessary func (ev *Event) GetProcessCapPermitted() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } return ev.BaseEvent.ProcessContext.Process.Credentials.CapPermitted } // GetProcessComm returns the value of the field, resolving if necessary func (ev *Event) GetProcessComm() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.BaseEvent.ProcessContext.Process.Comm } // GetProcessContainerId returns the value of the field, resolving if necessary func (ev *Event) GetProcessContainerId() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.BaseEvent.ProcessContext.Process.ContainerID } // GetProcessCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetProcessCreatedAt() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveProcessCreatedAt(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessEgid returns the value of the field, resolving if necessary func (ev *Event) GetProcessEgid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } return ev.BaseEvent.ProcessContext.Process.Credentials.EGID } // GetProcessEgroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessEgroup() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.BaseEvent.ProcessContext.Process.Credentials.EGroup } // GetProcessEnvp returns the value of the field, resolving if necessary func (ev *Event) GetProcessEnvp() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessEnvs returns the value of the field, resolving if necessary func (ev *Event) GetProcessEnvs() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessEnvsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetProcessEnvsTruncated() bool { - zeroValue := false if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return false } return ev.FieldHandlers.ResolveProcessEnvsTruncated(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessEuid returns the value of the field, resolving if necessary func (ev *Event) GetProcessEuid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } return ev.BaseEvent.ProcessContext.Process.Credentials.EUID } // GetProcessEuser returns the value of the field, resolving if necessary func (ev *Event) GetProcessEuser() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.BaseEvent.ProcessContext.Process.Credentials.EUser } // GetProcessExecTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessExecTime() time.Time { - zeroValue := time.Time{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return time.Time{} } return ev.BaseEvent.ProcessContext.Process.ExecTime } // GetProcessExitTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessExitTime() time.Time { - zeroValue := time.Time{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return time.Time{} } return ev.BaseEvent.ProcessContext.Process.ExitTime } // GetProcessFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return uint64(0) @@ -6119,9 +5631,8 @@ func (ev *Event) GetProcessFileChangeTime() uint64 { // GetProcessFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileFilesystem() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return "" @@ -6131,9 +5642,8 @@ func (ev *Event) GetProcessFileFilesystem() string { // GetProcessFileGid returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileGid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return uint32(0) @@ -6143,9 +5653,8 @@ func (ev *Event) GetProcessFileGid() uint32 { // GetProcessFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileGroup() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return "" @@ -6155,9 +5664,8 @@ func (ev *Event) GetProcessFileGroup() string { // GetProcessFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileHashes() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return []string{} @@ -6167,9 +5675,8 @@ func (ev *Event) GetProcessFileHashes() []string { // GetProcessFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileInUpperLayer() bool { - zeroValue := false if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return false } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return false @@ -6179,9 +5686,8 @@ func (ev *Event) GetProcessFileInUpperLayer() bool { // GetProcessFileInode returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileInode() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return uint64(0) @@ -6191,9 +5697,8 @@ func (ev *Event) GetProcessFileInode() uint64 { // GetProcessFileMode returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileMode() uint16 { - zeroValue := uint16(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint16(0) } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return uint16(0) @@ -6203,9 +5708,8 @@ func (ev *Event) GetProcessFileMode() uint16 { // GetProcessFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return uint64(0) @@ -6215,9 +5719,8 @@ func (ev *Event) GetProcessFileModificationTime() uint64 { // GetProcessFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileMountId() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return uint32(0) @@ -6227,9 +5730,8 @@ func (ev *Event) GetProcessFileMountId() uint32 { // GetProcessFileName returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileName() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return "" @@ -6239,18 +5741,16 @@ func (ev *Event) GetProcessFileName() string { // GetProcessFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileNameLength() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.BaseEvent.ProcessContext.Process.FileEvent)) } // GetProcessFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetProcessFilePackageName() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return "" @@ -6260,9 +5760,8 @@ func (ev *Event) GetProcessFilePackageName() string { // GetProcessFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetProcessFilePackageSourceVersion() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return "" @@ -6272,9 +5771,8 @@ func (ev *Event) GetProcessFilePackageSourceVersion() string { // GetProcessFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetProcessFilePackageVersion() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return "" @@ -6284,9 +5782,8 @@ func (ev *Event) GetProcessFilePackageVersion() string { // GetProcessFilePath returns the value of the field, resolving if necessary func (ev *Event) GetProcessFilePath() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return "" @@ -6296,18 +5793,16 @@ func (ev *Event) GetProcessFilePath() string { // GetProcessFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessFilePathLength() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.BaseEvent.ProcessContext.Process.FileEvent)) } // GetProcessFileRights returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileRights() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return 0 @@ -6317,9 +5812,8 @@ func (ev *Event) GetProcessFileRights() int { // GetProcessFileUid returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileUid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return uint32(0) @@ -6329,9 +5823,8 @@ func (ev *Event) GetProcessFileUid() uint32 { // GetProcessFileUser returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileUser() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.IsNotKworker() { return "" @@ -6341,72 +5834,64 @@ func (ev *Event) GetProcessFileUser() string { // GetProcessForkTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessForkTime() time.Time { - zeroValue := time.Time{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return time.Time{} } return ev.BaseEvent.ProcessContext.Process.ForkTime } // GetProcessFsgid returns the value of the field, resolving if necessary func (ev *Event) GetProcessFsgid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } return ev.BaseEvent.ProcessContext.Process.Credentials.FSGID } // GetProcessFsgroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessFsgroup() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.BaseEvent.ProcessContext.Process.Credentials.FSGroup } // GetProcessFsuid returns the value of the field, resolving if necessary func (ev *Event) GetProcessFsuid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } return ev.BaseEvent.ProcessContext.Process.Credentials.FSUID } // GetProcessFsuser returns the value of the field, resolving if necessary func (ev *Event) GetProcessFsuser() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.BaseEvent.ProcessContext.Process.Credentials.FSUser } // GetProcessGid returns the value of the field, resolving if necessary func (ev *Event) GetProcessGid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } return ev.BaseEvent.ProcessContext.Process.Credentials.GID } // GetProcessGroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessGroup() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.BaseEvent.ProcessContext.Process.Credentials.Group } // GetProcessInterpreterFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return uint64(0) @@ -6416,9 +5901,8 @@ func (ev *Event) GetProcessInterpreterFileChangeTime() uint64 { // GetProcessInterpreterFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileFilesystem() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return "" @@ -6428,9 +5912,8 @@ func (ev *Event) GetProcessInterpreterFileFilesystem() string { // GetProcessInterpreterFileGid returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileGid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return uint32(0) @@ -6440,9 +5923,8 @@ func (ev *Event) GetProcessInterpreterFileGid() uint32 { // GetProcessInterpreterFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileGroup() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return "" @@ -6452,9 +5934,8 @@ func (ev *Event) GetProcessInterpreterFileGroup() string { // GetProcessInterpreterFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileHashes() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return []string{} @@ -6464,9 +5945,8 @@ func (ev *Event) GetProcessInterpreterFileHashes() []string { // GetProcessInterpreterFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileInUpperLayer() bool { - zeroValue := false if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return false } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return false @@ -6476,9 +5956,8 @@ func (ev *Event) GetProcessInterpreterFileInUpperLayer() bool { // GetProcessInterpreterFileInode returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileInode() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return uint64(0) @@ -6488,9 +5967,8 @@ func (ev *Event) GetProcessInterpreterFileInode() uint64 { // GetProcessInterpreterFileMode returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileMode() uint16 { - zeroValue := uint16(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint16(0) } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return uint16(0) @@ -6500,9 +5978,8 @@ func (ev *Event) GetProcessInterpreterFileMode() uint16 { // GetProcessInterpreterFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return uint64(0) @@ -6512,9 +5989,8 @@ func (ev *Event) GetProcessInterpreterFileModificationTime() uint64 { // GetProcessInterpreterFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileMountId() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return uint32(0) @@ -6524,9 +6000,8 @@ func (ev *Event) GetProcessInterpreterFileMountId() uint32 { // GetProcessInterpreterFileName returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileName() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return "" @@ -6536,18 +6011,16 @@ func (ev *Event) GetProcessInterpreterFileName() string { // GetProcessInterpreterFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileNameLength() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.BaseEvent.ProcessContext.Process.LinuxBinprm.FileEvent)) } // GetProcessInterpreterFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFilePackageName() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return "" @@ -6557,9 +6030,8 @@ func (ev *Event) GetProcessInterpreterFilePackageName() string { // GetProcessInterpreterFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFilePackageSourceVersion() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return "" @@ -6569,9 +6041,8 @@ func (ev *Event) GetProcessInterpreterFilePackageSourceVersion() string { // GetProcessInterpreterFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFilePackageVersion() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return "" @@ -6581,9 +6052,8 @@ func (ev *Event) GetProcessInterpreterFilePackageVersion() string { // GetProcessInterpreterFilePath returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFilePath() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return "" @@ -6593,18 +6063,16 @@ func (ev *Event) GetProcessInterpreterFilePath() string { // GetProcessInterpreterFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFilePathLength() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.BaseEvent.ProcessContext.Process.LinuxBinprm.FileEvent)) } // GetProcessInterpreterFileRights returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileRights() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return 0 @@ -6614,9 +6082,8 @@ func (ev *Event) GetProcessInterpreterFileRights() int { // GetProcessInterpreterFileUid returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileUid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return uint32(0) @@ -6626,9 +6093,8 @@ func (ev *Event) GetProcessInterpreterFileUid() uint32 { // GetProcessInterpreterFileUser returns the value of the field, resolving if necessary func (ev *Event) GetProcessInterpreterFileUser() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.Process.HasInterpreter() { return "" @@ -6638,30 +6104,27 @@ func (ev *Event) GetProcessInterpreterFileUser() string { // GetProcessIsKworker returns the value of the field, resolving if necessary func (ev *Event) GetProcessIsKworker() bool { - zeroValue := false if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return false } return ev.BaseEvent.ProcessContext.Process.PIDContext.IsKworker } // GetProcessIsThread returns the value of the field, resolving if necessary func (ev *Event) GetProcessIsThread() bool { - zeroValue := false if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return false } return ev.BaseEvent.ProcessContext.Process.IsThread } // GetProcessParentArgs returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentArgs() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -6671,12 +6134,11 @@ func (ev *Event) GetProcessParentArgs() string { // GetProcessParentArgsFlags returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentArgsFlags() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} @@ -6686,12 +6148,11 @@ func (ev *Event) GetProcessParentArgsFlags() []string { // GetProcessParentArgsOptions returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentArgsOptions() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} @@ -6701,12 +6162,11 @@ func (ev *Event) GetProcessParentArgsOptions() []string { // GetProcessParentArgsScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentArgsScrubbed() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -6716,12 +6176,11 @@ func (ev *Event) GetProcessParentArgsScrubbed() string { // GetProcessParentArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentArgsTruncated() bool { - zeroValue := false if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return false } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return false } if !ev.BaseEvent.ProcessContext.HasParent() { return false @@ -6731,12 +6190,11 @@ func (ev *Event) GetProcessParentArgsTruncated() bool { // GetProcessParentArgv returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentArgv() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} @@ -6746,12 +6204,11 @@ func (ev *Event) GetProcessParentArgv() []string { // GetProcessParentArgv0 returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentArgv0() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -6761,12 +6218,11 @@ func (ev *Event) GetProcessParentArgv0() string { // GetProcessParentArgvScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentArgvScrubbed() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} @@ -6776,12 +6232,11 @@ func (ev *Event) GetProcessParentArgvScrubbed() []string { // GetProcessParentCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentCapEffective() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint64(0) @@ -6791,12 +6246,11 @@ func (ev *Event) GetProcessParentCapEffective() uint64 { // GetProcessParentCapPermitted returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentCapPermitted() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint64(0) @@ -6806,12 +6260,11 @@ func (ev *Event) GetProcessParentCapPermitted() uint64 { // GetProcessParentComm returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentComm() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -6821,12 +6274,11 @@ func (ev *Event) GetProcessParentComm() string { // GetProcessParentContainerId returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentContainerId() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -6836,12 +6288,11 @@ func (ev *Event) GetProcessParentContainerId() string { // GetProcessParentCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentCreatedAt() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return 0 } if !ev.BaseEvent.ProcessContext.HasParent() { return 0 @@ -6851,12 +6302,11 @@ func (ev *Event) GetProcessParentCreatedAt() int { // GetProcessParentEgid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentEgid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -6866,12 +6316,11 @@ func (ev *Event) GetProcessParentEgid() uint32 { // GetProcessParentEgroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentEgroup() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -6881,12 +6330,11 @@ func (ev *Event) GetProcessParentEgroup() string { // GetProcessParentEnvp returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentEnvp() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} @@ -6896,12 +6344,11 @@ func (ev *Event) GetProcessParentEnvp() []string { // GetProcessParentEnvs returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentEnvs() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} @@ -6911,12 +6358,11 @@ func (ev *Event) GetProcessParentEnvs() []string { // GetProcessParentEnvsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentEnvsTruncated() bool { - zeroValue := false if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return false } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return false } if !ev.BaseEvent.ProcessContext.HasParent() { return false @@ -6926,12 +6372,11 @@ func (ev *Event) GetProcessParentEnvsTruncated() bool { // GetProcessParentEuid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentEuid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -6941,12 +6386,11 @@ func (ev *Event) GetProcessParentEuid() uint32 { // GetProcessParentEuser returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentEuser() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -6956,12 +6400,11 @@ func (ev *Event) GetProcessParentEuser() string { // GetProcessParentFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint64(0) @@ -6974,12 +6417,11 @@ func (ev *Event) GetProcessParentFileChangeTime() uint64 { // GetProcessParentFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileFilesystem() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -6992,12 +6434,11 @@ func (ev *Event) GetProcessParentFileFilesystem() string { // GetProcessParentFileGid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileGid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7010,12 +6451,11 @@ func (ev *Event) GetProcessParentFileGid() uint32 { // GetProcessParentFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileGroup() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7028,12 +6468,11 @@ func (ev *Event) GetProcessParentFileGroup() string { // GetProcessParentFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileHashes() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} @@ -7046,12 +6485,11 @@ func (ev *Event) GetProcessParentFileHashes() []string { // GetProcessParentFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileInUpperLayer() bool { - zeroValue := false if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return false } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return false } if !ev.BaseEvent.ProcessContext.HasParent() { return false @@ -7064,12 +6502,11 @@ func (ev *Event) GetProcessParentFileInUpperLayer() bool { // GetProcessParentFileInode returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileInode() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint64(0) @@ -7082,12 +6519,11 @@ func (ev *Event) GetProcessParentFileInode() uint64 { // GetProcessParentFileMode returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileMode() uint16 { - zeroValue := uint16(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint16(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint16(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint16(0) @@ -7100,12 +6536,11 @@ func (ev *Event) GetProcessParentFileMode() uint16 { // GetProcessParentFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint64(0) @@ -7118,12 +6553,11 @@ func (ev *Event) GetProcessParentFileModificationTime() uint64 { // GetProcessParentFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileMountId() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7136,12 +6570,11 @@ func (ev *Event) GetProcessParentFileMountId() uint32 { // GetProcessParentFileName returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileName() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7154,24 +6587,22 @@ func (ev *Event) GetProcessParentFileName() string { // GetProcessParentFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileNameLength() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.BaseEvent.ProcessContext.Parent.FileEvent)) } // GetProcessParentFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFilePackageName() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7184,12 +6615,11 @@ func (ev *Event) GetProcessParentFilePackageName() string { // GetProcessParentFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFilePackageSourceVersion() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7202,12 +6632,11 @@ func (ev *Event) GetProcessParentFilePackageSourceVersion() string { // GetProcessParentFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFilePackageVersion() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7220,12 +6649,11 @@ func (ev *Event) GetProcessParentFilePackageVersion() string { // GetProcessParentFilePath returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFilePath() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7238,24 +6666,22 @@ func (ev *Event) GetProcessParentFilePath() string { // GetProcessParentFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFilePathLength() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.BaseEvent.ProcessContext.Parent.FileEvent)) } // GetProcessParentFileRights returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileRights() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return 0 } if !ev.BaseEvent.ProcessContext.HasParent() { return 0 @@ -7268,12 +6694,11 @@ func (ev *Event) GetProcessParentFileRights() int { // GetProcessParentFileUid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileUid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7286,12 +6711,11 @@ func (ev *Event) GetProcessParentFileUid() uint32 { // GetProcessParentFileUser returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileUser() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7304,12 +6728,11 @@ func (ev *Event) GetProcessParentFileUser() string { // GetProcessParentFsgid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFsgid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7319,12 +6742,11 @@ func (ev *Event) GetProcessParentFsgid() uint32 { // GetProcessParentFsgroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFsgroup() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7334,12 +6756,11 @@ func (ev *Event) GetProcessParentFsgroup() string { // GetProcessParentFsuid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFsuid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7349,12 +6770,11 @@ func (ev *Event) GetProcessParentFsuid() uint32 { // GetProcessParentFsuser returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFsuser() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7364,12 +6784,11 @@ func (ev *Event) GetProcessParentFsuser() string { // GetProcessParentGid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentGid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7379,12 +6798,11 @@ func (ev *Event) GetProcessParentGid() uint32 { // GetProcessParentGroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentGroup() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7394,12 +6812,11 @@ func (ev *Event) GetProcessParentGroup() string { // GetProcessParentInterpreterFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint64(0) @@ -7412,12 +6829,11 @@ func (ev *Event) GetProcessParentInterpreterFileChangeTime() uint64 { // GetProcessParentInterpreterFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileFilesystem() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7430,12 +6846,11 @@ func (ev *Event) GetProcessParentInterpreterFileFilesystem() string { // GetProcessParentInterpreterFileGid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileGid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7448,12 +6863,11 @@ func (ev *Event) GetProcessParentInterpreterFileGid() uint32 { // GetProcessParentInterpreterFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileGroup() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7466,12 +6880,11 @@ func (ev *Event) GetProcessParentInterpreterFileGroup() string { // GetProcessParentInterpreterFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileHashes() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} @@ -7484,12 +6897,11 @@ func (ev *Event) GetProcessParentInterpreterFileHashes() []string { // GetProcessParentInterpreterFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileInUpperLayer() bool { - zeroValue := false if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return false } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return false } if !ev.BaseEvent.ProcessContext.HasParent() { return false @@ -7502,12 +6914,11 @@ func (ev *Event) GetProcessParentInterpreterFileInUpperLayer() bool { // GetProcessParentInterpreterFileInode returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileInode() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint64(0) @@ -7520,12 +6931,11 @@ func (ev *Event) GetProcessParentInterpreterFileInode() uint64 { // GetProcessParentInterpreterFileMode returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileMode() uint16 { - zeroValue := uint16(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint16(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint16(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint16(0) @@ -7538,12 +6948,11 @@ func (ev *Event) GetProcessParentInterpreterFileMode() uint16 { // GetProcessParentInterpreterFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint64(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint64(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint64(0) @@ -7556,12 +6965,11 @@ func (ev *Event) GetProcessParentInterpreterFileModificationTime() uint64 { // GetProcessParentInterpreterFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileMountId() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7574,12 +6982,11 @@ func (ev *Event) GetProcessParentInterpreterFileMountId() uint32 { // GetProcessParentInterpreterFileName returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileName() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7592,24 +6999,22 @@ func (ev *Event) GetProcessParentInterpreterFileName() string { // GetProcessParentInterpreterFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileNameLength() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.BaseEvent.ProcessContext.Parent.LinuxBinprm.FileEvent)) } // GetProcessParentInterpreterFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFilePackageName() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7622,12 +7027,11 @@ func (ev *Event) GetProcessParentInterpreterFilePackageName() string { // GetProcessParentInterpreterFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFilePackageSourceVersion() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7640,12 +7044,11 @@ func (ev *Event) GetProcessParentInterpreterFilePackageSourceVersion() string { // GetProcessParentInterpreterFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFilePackageVersion() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7658,12 +7061,11 @@ func (ev *Event) GetProcessParentInterpreterFilePackageVersion() string { // GetProcessParentInterpreterFilePath returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFilePath() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7676,24 +7078,22 @@ func (ev *Event) GetProcessParentInterpreterFilePath() string { // GetProcessParentInterpreterFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFilePathLength() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.BaseEvent.ProcessContext.Parent.LinuxBinprm.FileEvent)) } // GetProcessParentInterpreterFileRights returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileRights() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return 0 } if !ev.BaseEvent.ProcessContext.HasParent() { return 0 @@ -7706,12 +7106,11 @@ func (ev *Event) GetProcessParentInterpreterFileRights() int { // GetProcessParentInterpreterFileUid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileUid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7724,12 +7123,11 @@ func (ev *Event) GetProcessParentInterpreterFileUid() uint32 { // GetProcessParentInterpreterFileUser returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentInterpreterFileUser() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7742,12 +7140,11 @@ func (ev *Event) GetProcessParentInterpreterFileUser() string { // GetProcessParentIsKworker returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentIsKworker() bool { - zeroValue := false if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return false } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return false } if !ev.BaseEvent.ProcessContext.HasParent() { return false @@ -7757,12 +7154,11 @@ func (ev *Event) GetProcessParentIsKworker() bool { // GetProcessParentIsThread returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentIsThread() bool { - zeroValue := false if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return false } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return false } if !ev.BaseEvent.ProcessContext.HasParent() { return false @@ -7772,12 +7168,11 @@ func (ev *Event) GetProcessParentIsThread() bool { // GetProcessParentPid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentPid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7787,12 +7182,11 @@ func (ev *Event) GetProcessParentPid() uint32 { // GetProcessParentPpid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentPpid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7802,12 +7196,11 @@ func (ev *Event) GetProcessParentPpid() uint32 { // GetProcessParentTid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentTid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7817,12 +7210,11 @@ func (ev *Event) GetProcessParentTid() uint32 { // GetProcessParentTtyName returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentTtyName() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7832,12 +7224,11 @@ func (ev *Event) GetProcessParentTtyName() string { // GetProcessParentUid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentUid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -7847,12 +7238,11 @@ func (ev *Event) GetProcessParentUid() uint32 { // GetProcessParentUser returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentUser() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7862,12 +7252,11 @@ func (ev *Event) GetProcessParentUser() string { // GetProcessParentUserSessionK8sGroups returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentUserSessionK8sGroups() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} @@ -7877,12 +7266,11 @@ func (ev *Event) GetProcessParentUserSessionK8sGroups() []string { // GetProcessParentUserSessionK8sUid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentUserSessionK8sUid() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7892,12 +7280,11 @@ func (ev *Event) GetProcessParentUserSessionK8sUid() string { // GetProcessParentUserSessionK8sUsername returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentUserSessionK8sUsername() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -7907,114 +7294,102 @@ func (ev *Event) GetProcessParentUserSessionK8sUsername() string { // GetProcessPid returns the value of the field, resolving if necessary func (ev *Event) GetProcessPid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } return ev.BaseEvent.ProcessContext.Process.PIDContext.Pid } // GetProcessPpid returns the value of the field, resolving if necessary func (ev *Event) GetProcessPpid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } return ev.BaseEvent.ProcessContext.Process.PPid } // GetProcessTid returns the value of the field, resolving if necessary func (ev *Event) GetProcessTid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } return ev.BaseEvent.ProcessContext.Process.PIDContext.Tid } // GetProcessTtyName returns the value of the field, resolving if necessary func (ev *Event) GetProcessTtyName() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.BaseEvent.ProcessContext.Process.TTYName } // GetProcessUid returns the value of the field, resolving if necessary func (ev *Event) GetProcessUid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } return ev.BaseEvent.ProcessContext.Process.Credentials.UID } // GetProcessUser returns the value of the field, resolving if necessary func (ev *Event) GetProcessUser() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.BaseEvent.ProcessContext.Process.Credentials.User } // GetProcessUserSessionK8sGroups returns the value of the field, resolving if necessary func (ev *Event) GetProcessUserSessionK8sGroups() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.BaseEvent.ProcessContext.Process.UserSession) } // GetProcessUserSessionK8sUid returns the value of the field, resolving if necessary func (ev *Event) GetProcessUserSessionK8sUid() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveK8SUID(ev, &ev.BaseEvent.ProcessContext.Process.UserSession) } // GetProcessUserSessionK8sUsername returns the value of the field, resolving if necessary func (ev *Event) GetProcessUserSessionK8sUsername() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveK8SUsername(ev, &ev.BaseEvent.ProcessContext.Process.UserSession) } // GetPtraceRequest returns the value of the field, resolving if necessary func (ev *Event) GetPtraceRequest() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } return ev.PTrace.Request } // GetPtraceRetval returns the value of the field, resolving if necessary func (ev *Event) GetPtraceRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return int64(0) } return ev.PTrace.SyscallEvent.Retval } // GetPtraceTraceeAncestorsArgs returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsArgs() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8031,15 +7406,14 @@ func (ev *Event) GetPtraceTraceeAncestorsArgs() []string { // GetPtraceTraceeAncestorsArgsFlags returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsArgsFlags() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8056,15 +7430,14 @@ func (ev *Event) GetPtraceTraceeAncestorsArgsFlags() []string { // GetPtraceTraceeAncestorsArgsOptions returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsArgsOptions() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8081,15 +7454,14 @@ func (ev *Event) GetPtraceTraceeAncestorsArgsOptions() []string { // GetPtraceTraceeAncestorsArgsScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsArgsScrubbed() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8106,15 +7478,14 @@ func (ev *Event) GetPtraceTraceeAncestorsArgsScrubbed() []string { // GetPtraceTraceeAncestorsArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsArgsTruncated() []bool { - zeroValue := []bool{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []bool{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []bool{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -8131,15 +7502,14 @@ func (ev *Event) GetPtraceTraceeAncestorsArgsTruncated() []bool { // GetPtraceTraceeAncestorsArgv returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsArgv() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8156,15 +7526,14 @@ func (ev *Event) GetPtraceTraceeAncestorsArgv() []string { // GetPtraceTraceeAncestorsArgv0 returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsArgv0() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8181,15 +7550,14 @@ func (ev *Event) GetPtraceTraceeAncestorsArgv0() []string { // GetPtraceTraceeAncestorsArgvScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsArgvScrubbed() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8206,15 +7574,14 @@ func (ev *Event) GetPtraceTraceeAncestorsArgvScrubbed() []string { // GetPtraceTraceeAncestorsCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsCapEffective() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -8231,15 +7598,14 @@ func (ev *Event) GetPtraceTraceeAncestorsCapEffective() []uint64 { // GetPtraceTraceeAncestorsCapPermitted returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsCapPermitted() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -8256,15 +7622,14 @@ func (ev *Event) GetPtraceTraceeAncestorsCapPermitted() []uint64 { // GetPtraceTraceeAncestorsComm returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsComm() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8281,15 +7646,14 @@ func (ev *Event) GetPtraceTraceeAncestorsComm() []string { // GetPtraceTraceeAncestorsContainerId returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsContainerId() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8306,15 +7670,14 @@ func (ev *Event) GetPtraceTraceeAncestorsContainerId() []string { // GetPtraceTraceeAncestorsCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsCreatedAt() []int { - zeroValue := []int{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []int{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []int{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -8331,15 +7694,14 @@ func (ev *Event) GetPtraceTraceeAncestorsCreatedAt() []int { // GetPtraceTraceeAncestorsEgid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsEgid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -8356,15 +7718,14 @@ func (ev *Event) GetPtraceTraceeAncestorsEgid() []uint32 { // GetPtraceTraceeAncestorsEgroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsEgroup() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8381,15 +7742,14 @@ func (ev *Event) GetPtraceTraceeAncestorsEgroup() []string { // GetPtraceTraceeAncestorsEnvp returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsEnvp() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8406,15 +7766,14 @@ func (ev *Event) GetPtraceTraceeAncestorsEnvp() []string { // GetPtraceTraceeAncestorsEnvs returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsEnvs() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8431,15 +7790,14 @@ func (ev *Event) GetPtraceTraceeAncestorsEnvs() []string { // GetPtraceTraceeAncestorsEnvsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsEnvsTruncated() []bool { - zeroValue := []bool{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []bool{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []bool{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -8456,15 +7814,14 @@ func (ev *Event) GetPtraceTraceeAncestorsEnvsTruncated() []bool { // GetPtraceTraceeAncestorsEuid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsEuid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -8481,15 +7838,14 @@ func (ev *Event) GetPtraceTraceeAncestorsEuid() []uint32 { // GetPtraceTraceeAncestorsEuser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsEuser() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8506,15 +7862,14 @@ func (ev *Event) GetPtraceTraceeAncestorsEuser() []string { // GetPtraceTraceeAncestorsFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileChangeTime() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -8531,15 +7886,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileChangeTime() []uint64 { // GetPtraceTraceeAncestorsFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileFilesystem() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8556,15 +7910,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileFilesystem() []string { // GetPtraceTraceeAncestorsFileGid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileGid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -8581,15 +7934,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileGid() []uint32 { // GetPtraceTraceeAncestorsFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileGroup() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8606,15 +7958,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileGroup() []string { // GetPtraceTraceeAncestorsFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8631,15 +7982,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileHashes() []string { // GetPtraceTraceeAncestorsFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileInUpperLayer() []bool { - zeroValue := []bool{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []bool{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []bool{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -8656,15 +8006,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileInUpperLayer() []bool { // GetPtraceTraceeAncestorsFileInode returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileInode() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -8681,15 +8030,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileInode() []uint64 { // GetPtraceTraceeAncestorsFileMode returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileMode() []uint16 { - zeroValue := []uint16{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint16{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint16{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint16{} } var values []uint16 ctx := eval.NewContext(ev) @@ -8706,15 +8054,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileMode() []uint16 { // GetPtraceTraceeAncestorsFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileModificationTime() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -8731,15 +8078,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileModificationTime() []uint64 { // GetPtraceTraceeAncestorsFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileMountId() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -8756,15 +8102,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileMountId() []uint32 { // GetPtraceTraceeAncestorsFileName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileName() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8781,15 +8126,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileName() []string { // GetPtraceTraceeAncestorsFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileNameLength() []int { - zeroValue := []int{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []int{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []int{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -8806,15 +8150,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileNameLength() []int { // GetPtraceTraceeAncestorsFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFilePackageName() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8831,15 +8174,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFilePackageName() []string { // GetPtraceTraceeAncestorsFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFilePackageSourceVersion() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8856,15 +8198,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFilePackageSourceVersion() []string { // GetPtraceTraceeAncestorsFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFilePackageVersion() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8881,15 +8222,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFilePackageVersion() []string { // GetPtraceTraceeAncestorsFilePath returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFilePath() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -8906,15 +8246,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFilePath() []string { // GetPtraceTraceeAncestorsFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFilePathLength() []int { - zeroValue := []int{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []int{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []int{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -8931,15 +8270,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFilePathLength() []int { // GetPtraceTraceeAncestorsFileRights returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileRights() []int { - zeroValue := []int{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []int{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []int{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -8956,15 +8294,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileRights() []int { // GetPtraceTraceeAncestorsFileUid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileUid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -8981,15 +8318,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileUid() []uint32 { // GetPtraceTraceeAncestorsFileUser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFileUser() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9006,15 +8342,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFileUser() []string { // GetPtraceTraceeAncestorsFsgid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFsgid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -9031,15 +8366,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFsgid() []uint32 { // GetPtraceTraceeAncestorsFsgroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFsgroup() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9056,15 +8390,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFsgroup() []string { // GetPtraceTraceeAncestorsFsuid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFsuid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -9081,15 +8414,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFsuid() []uint32 { // GetPtraceTraceeAncestorsFsuser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsFsuser() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9106,15 +8438,14 @@ func (ev *Event) GetPtraceTraceeAncestorsFsuser() []string { // GetPtraceTraceeAncestorsGid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsGid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -9131,15 +8462,14 @@ func (ev *Event) GetPtraceTraceeAncestorsGid() []uint32 { // GetPtraceTraceeAncestorsGroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsGroup() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9156,15 +8486,14 @@ func (ev *Event) GetPtraceTraceeAncestorsGroup() []string { // GetPtraceTraceeAncestorsInterpreterFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileChangeTime() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -9181,15 +8510,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileChangeTime() []uint64 { // GetPtraceTraceeAncestorsInterpreterFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileFilesystem() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9206,15 +8534,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileFilesystem() []string { // GetPtraceTraceeAncestorsInterpreterFileGid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileGid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -9231,15 +8558,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileGid() []uint32 { // GetPtraceTraceeAncestorsInterpreterFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileGroup() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9256,15 +8582,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileGroup() []string { // GetPtraceTraceeAncestorsInterpreterFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9281,15 +8606,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileHashes() []string { // GetPtraceTraceeAncestorsInterpreterFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileInUpperLayer() []bool { - zeroValue := []bool{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []bool{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []bool{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -9306,15 +8630,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileInUpperLayer() []bool { // GetPtraceTraceeAncestorsInterpreterFileInode returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileInode() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -9331,15 +8654,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileInode() []uint64 { // GetPtraceTraceeAncestorsInterpreterFileMode returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileMode() []uint16 { - zeroValue := []uint16{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint16{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint16{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint16{} } var values []uint16 ctx := eval.NewContext(ev) @@ -9356,15 +8678,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileMode() []uint16 { // GetPtraceTraceeAncestorsInterpreterFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileModificationTime() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint64{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -9381,15 +8702,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileModificationTime() []uin // GetPtraceTraceeAncestorsInterpreterFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileMountId() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -9406,15 +8726,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileMountId() []uint32 { // GetPtraceTraceeAncestorsInterpreterFileName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileName() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9431,15 +8750,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileName() []string { // GetPtraceTraceeAncestorsInterpreterFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileNameLength() []int { - zeroValue := []int{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []int{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []int{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -9456,15 +8774,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileNameLength() []int { // GetPtraceTraceeAncestorsInterpreterFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFilePackageName() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9481,15 +8798,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFilePackageName() []string { // GetPtraceTraceeAncestorsInterpreterFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFilePackageSourceVersion() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9506,15 +8822,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFilePackageSourceVersion() [ // GetPtraceTraceeAncestorsInterpreterFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFilePackageVersion() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9531,15 +8846,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFilePackageVersion() []strin // GetPtraceTraceeAncestorsInterpreterFilePath returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFilePath() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9556,15 +8870,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFilePath() []string { // GetPtraceTraceeAncestorsInterpreterFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFilePathLength() []int { - zeroValue := []int{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []int{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []int{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -9581,15 +8894,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFilePathLength() []int { // GetPtraceTraceeAncestorsInterpreterFileRights returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileRights() []int { - zeroValue := []int{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []int{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []int{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -9606,15 +8918,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileRights() []int { // GetPtraceTraceeAncestorsInterpreterFileUid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileUid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -9631,15 +8942,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileUid() []uint32 { // GetPtraceTraceeAncestorsInterpreterFileUser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileUser() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9656,15 +8966,14 @@ func (ev *Event) GetPtraceTraceeAncestorsInterpreterFileUser() []string { // GetPtraceTraceeAncestorsIsKworker returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsIsKworker() []bool { - zeroValue := []bool{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []bool{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []bool{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -9681,15 +8990,14 @@ func (ev *Event) GetPtraceTraceeAncestorsIsKworker() []bool { // GetPtraceTraceeAncestorsIsThread returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsIsThread() []bool { - zeroValue := []bool{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []bool{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []bool{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -9706,15 +9014,14 @@ func (ev *Event) GetPtraceTraceeAncestorsIsThread() []bool { // GetPtraceTraceeAncestorsPid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsPid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -9731,15 +9038,14 @@ func (ev *Event) GetPtraceTraceeAncestorsPid() []uint32 { // GetPtraceTraceeAncestorsPpid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsPpid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -9756,15 +9062,14 @@ func (ev *Event) GetPtraceTraceeAncestorsPpid() []uint32 { // GetPtraceTraceeAncestorsTid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsTid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -9781,15 +9086,14 @@ func (ev *Event) GetPtraceTraceeAncestorsTid() []uint32 { // GetPtraceTraceeAncestorsTtyName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsTtyName() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9806,15 +9110,14 @@ func (ev *Event) GetPtraceTraceeAncestorsTtyName() []string { // GetPtraceTraceeAncestorsUid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsUid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []uint32{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -9831,15 +9134,14 @@ func (ev *Event) GetPtraceTraceeAncestorsUid() []uint32 { // GetPtraceTraceeAncestorsUser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsUser() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9856,15 +9158,14 @@ func (ev *Event) GetPtraceTraceeAncestorsUser() []string { // GetPtraceTraceeAncestorsUserSessionK8sGroups returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsUserSessionK8sGroups() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9881,15 +9182,14 @@ func (ev *Event) GetPtraceTraceeAncestorsUserSessionK8sGroups() []string { // GetPtraceTraceeAncestorsUserSessionK8sUid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsUserSessionK8sUid() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9906,15 +9206,14 @@ func (ev *Event) GetPtraceTraceeAncestorsUserSessionK8sUid() []string { // GetPtraceTraceeAncestorsUserSessionK8sUsername returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeAncestorsUserSessionK8sUsername() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -9931,276 +9230,253 @@ func (ev *Event) GetPtraceTraceeAncestorsUserSessionK8sUsername() []string { // GetPtraceTraceeArgs returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeArgs() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgs(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeArgsFlags returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeArgsFlags() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgsFlags(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeArgsOptions returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeArgsOptions() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgsOptions(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeArgsScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeArgsScrubbed() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeArgsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "ptrace" { - return zeroValue + return false } if ev.PTrace.Tracee == nil { - return zeroValue + return false } return ev.FieldHandlers.ResolveProcessArgsTruncated(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeArgv returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeArgv() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgv(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeArgv0 returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeArgv0() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgv0(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeArgvScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeArgvScrubbed() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeCapEffective() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } return ev.PTrace.Tracee.Process.Credentials.CapEffective } // GetPtraceTraceeCapPermitted returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeCapPermitted() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } return ev.PTrace.Tracee.Process.Credentials.CapPermitted } // GetPtraceTraceeComm returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeComm() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.PTrace.Tracee.Process.Comm } // GetPtraceTraceeContainerId returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeContainerId() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.PTrace.Tracee.Process.ContainerID } // GetPtraceTraceeCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeCreatedAt() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveProcessCreatedAt(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeEgid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeEgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } return ev.PTrace.Tracee.Process.Credentials.EGID } // GetPtraceTraceeEgroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeEgroup() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.PTrace.Tracee.Process.Credentials.EGroup } // GetPtraceTraceeEnvp returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeEnvp() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeEnvs returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeEnvs() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeEnvsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeEnvsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "ptrace" { - return zeroValue + return false } if ev.PTrace.Tracee == nil { - return zeroValue + return false } return ev.FieldHandlers.ResolveProcessEnvsTruncated(ev, &ev.PTrace.Tracee.Process) } // GetPtraceTraceeEuid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeEuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } return ev.PTrace.Tracee.Process.Credentials.EUID } // GetPtraceTraceeEuser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeEuser() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.PTrace.Tracee.Process.Credentials.EUser } // GetPtraceTraceeExecTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeExecTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return time.Time{} } if ev.PTrace.Tracee == nil { - return zeroValue + return time.Time{} } return ev.PTrace.Tracee.Process.ExecTime } // GetPtraceTraceeExitTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeExitTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return time.Time{} } if ev.PTrace.Tracee == nil { - return zeroValue + return time.Time{} } return ev.PTrace.Tracee.Process.ExitTime } // GetPtraceTraceeFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.Process.IsNotKworker() { return uint64(0) @@ -10210,12 +9486,11 @@ func (ev *Event) GetPtraceTraceeFileChangeTime() uint64 { // GetPtraceTraceeFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.IsNotKworker() { return "" @@ -10225,12 +9500,11 @@ func (ev *Event) GetPtraceTraceeFileFilesystem() string { // GetPtraceTraceeFileGid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.Process.IsNotKworker() { return uint32(0) @@ -10240,12 +9514,11 @@ func (ev *Event) GetPtraceTraceeFileGid() uint32 { // GetPtraceTraceeFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.IsNotKworker() { return "" @@ -10255,12 +9528,11 @@ func (ev *Event) GetPtraceTraceeFileGroup() string { // GetPtraceTraceeFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if !ev.PTrace.Tracee.Process.IsNotKworker() { return []string{} @@ -10270,12 +9542,11 @@ func (ev *Event) GetPtraceTraceeFileHashes() []string { // GetPtraceTraceeFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "ptrace" { - return zeroValue + return false } if ev.PTrace.Tracee == nil { - return zeroValue + return false } if !ev.PTrace.Tracee.Process.IsNotKworker() { return false @@ -10285,12 +9556,11 @@ func (ev *Event) GetPtraceTraceeFileInUpperLayer() bool { // GetPtraceTraceeFileInode returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.Process.IsNotKworker() { return uint64(0) @@ -10300,12 +9570,11 @@ func (ev *Event) GetPtraceTraceeFileInode() uint64 { // GetPtraceTraceeFileMode returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint16(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint16(0) } if !ev.PTrace.Tracee.Process.IsNotKworker() { return uint16(0) @@ -10315,12 +9584,11 @@ func (ev *Event) GetPtraceTraceeFileMode() uint16 { // GetPtraceTraceeFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.Process.IsNotKworker() { return uint64(0) @@ -10330,12 +9598,11 @@ func (ev *Event) GetPtraceTraceeFileModificationTime() uint64 { // GetPtraceTraceeFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.Process.IsNotKworker() { return uint32(0) @@ -10345,12 +9612,11 @@ func (ev *Event) GetPtraceTraceeFileMountId() uint32 { // GetPtraceTraceeFileName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileName() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.IsNotKworker() { return "" @@ -10360,24 +9626,22 @@ func (ev *Event) GetPtraceTraceeFileName() string { // GetPtraceTraceeFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.PTrace.Tracee.Process.FileEvent)) } // GetPtraceTraceeFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.IsNotKworker() { return "" @@ -10387,12 +9651,11 @@ func (ev *Event) GetPtraceTraceeFilePackageName() string { // GetPtraceTraceeFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.IsNotKworker() { return "" @@ -10402,12 +9665,11 @@ func (ev *Event) GetPtraceTraceeFilePackageSourceVersion() string { // GetPtraceTraceeFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.IsNotKworker() { return "" @@ -10417,12 +9679,11 @@ func (ev *Event) GetPtraceTraceeFilePackageVersion() string { // GetPtraceTraceeFilePath returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.IsNotKworker() { return "" @@ -10432,24 +9693,22 @@ func (ev *Event) GetPtraceTraceeFilePath() string { // GetPtraceTraceeFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.PTrace.Tracee.Process.FileEvent)) } // GetPtraceTraceeFileRights returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } if !ev.PTrace.Tracee.Process.IsNotKworker() { return 0 @@ -10459,12 +9718,11 @@ func (ev *Event) GetPtraceTraceeFileRights() int { // GetPtraceTraceeFileUid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.Process.IsNotKworker() { return uint32(0) @@ -10474,12 +9732,11 @@ func (ev *Event) GetPtraceTraceeFileUid() uint32 { // GetPtraceTraceeFileUser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.IsNotKworker() { return "" @@ -10489,96 +9746,88 @@ func (ev *Event) GetPtraceTraceeFileUser() string { // GetPtraceTraceeForkTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeForkTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return time.Time{} } if ev.PTrace.Tracee == nil { - return zeroValue + return time.Time{} } return ev.PTrace.Tracee.Process.ForkTime } // GetPtraceTraceeFsgid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFsgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } return ev.PTrace.Tracee.Process.Credentials.FSGID } // GetPtraceTraceeFsgroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFsgroup() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.PTrace.Tracee.Process.Credentials.FSGroup } // GetPtraceTraceeFsuid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFsuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } return ev.PTrace.Tracee.Process.Credentials.FSUID } // GetPtraceTraceeFsuser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeFsuser() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.PTrace.Tracee.Process.Credentials.FSUser } // GetPtraceTraceeGid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } return ev.PTrace.Tracee.Process.Credentials.GID } // GetPtraceTraceeGroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeGroup() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.PTrace.Tracee.Process.Credentials.Group } // GetPtraceTraceeInterpreterFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.Process.HasInterpreter() { return uint64(0) @@ -10588,12 +9837,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFileChangeTime() uint64 { // GetPtraceTraceeInterpreterFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.HasInterpreter() { return "" @@ -10603,12 +9851,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFileFilesystem() string { // GetPtraceTraceeInterpreterFileGid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.Process.HasInterpreter() { return uint32(0) @@ -10618,12 +9865,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFileGid() uint32 { // GetPtraceTraceeInterpreterFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.HasInterpreter() { return "" @@ -10633,12 +9879,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFileGroup() string { // GetPtraceTraceeInterpreterFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if !ev.PTrace.Tracee.Process.HasInterpreter() { return []string{} @@ -10648,12 +9893,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFileHashes() []string { // GetPtraceTraceeInterpreterFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "ptrace" { - return zeroValue + return false } if ev.PTrace.Tracee == nil { - return zeroValue + return false } if !ev.PTrace.Tracee.Process.HasInterpreter() { return false @@ -10663,12 +9907,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFileInUpperLayer() bool { // GetPtraceTraceeInterpreterFileInode returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.Process.HasInterpreter() { return uint64(0) @@ -10678,12 +9921,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFileInode() uint64 { // GetPtraceTraceeInterpreterFileMode returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint16(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint16(0) } if !ev.PTrace.Tracee.Process.HasInterpreter() { return uint16(0) @@ -10693,12 +9935,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFileMode() uint16 { // GetPtraceTraceeInterpreterFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.Process.HasInterpreter() { return uint64(0) @@ -10708,12 +9949,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFileModificationTime() uint64 { // GetPtraceTraceeInterpreterFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.Process.HasInterpreter() { return uint32(0) @@ -10723,12 +9963,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFileMountId() uint32 { // GetPtraceTraceeInterpreterFileName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileName() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.HasInterpreter() { return "" @@ -10738,24 +9977,22 @@ func (ev *Event) GetPtraceTraceeInterpreterFileName() string { // GetPtraceTraceeInterpreterFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.PTrace.Tracee.Process.LinuxBinprm.FileEvent)) } // GetPtraceTraceeInterpreterFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.HasInterpreter() { return "" @@ -10765,12 +10002,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFilePackageName() string { // GetPtraceTraceeInterpreterFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.HasInterpreter() { return "" @@ -10780,12 +10016,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFilePackageSourceVersion() string { // GetPtraceTraceeInterpreterFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.HasInterpreter() { return "" @@ -10795,12 +10030,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFilePackageVersion() string { // GetPtraceTraceeInterpreterFilePath returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.HasInterpreter() { return "" @@ -10810,24 +10044,22 @@ func (ev *Event) GetPtraceTraceeInterpreterFilePath() string { // GetPtraceTraceeInterpreterFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.PTrace.Tracee.Process.LinuxBinprm.FileEvent)) } // GetPtraceTraceeInterpreterFileRights returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } if !ev.PTrace.Tracee.Process.HasInterpreter() { return 0 @@ -10837,12 +10069,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFileRights() int { // GetPtraceTraceeInterpreterFileUid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.Process.HasInterpreter() { return uint32(0) @@ -10852,12 +10083,11 @@ func (ev *Event) GetPtraceTraceeInterpreterFileUid() uint32 { // GetPtraceTraceeInterpreterFileUser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeInterpreterFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.Process.HasInterpreter() { return "" @@ -10867,39 +10097,36 @@ func (ev *Event) GetPtraceTraceeInterpreterFileUser() string { // GetPtraceTraceeIsKworker returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeIsKworker() bool { - zeroValue := false if ev.GetEventType().String() != "ptrace" { - return zeroValue + return false } if ev.PTrace.Tracee == nil { - return zeroValue + return false } return ev.PTrace.Tracee.Process.PIDContext.IsKworker } // GetPtraceTraceeIsThread returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeIsThread() bool { - zeroValue := false if ev.GetEventType().String() != "ptrace" { - return zeroValue + return false } if ev.PTrace.Tracee == nil { - return zeroValue + return false } return ev.PTrace.Tracee.Process.IsThread } // GetPtraceTraceeParentArgs returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentArgs() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -10909,15 +10136,14 @@ func (ev *Event) GetPtraceTraceeParentArgs() string { // GetPtraceTraceeParentArgsFlags returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentArgsFlags() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return []string{} } if !ev.PTrace.Tracee.HasParent() { return []string{} @@ -10927,15 +10153,14 @@ func (ev *Event) GetPtraceTraceeParentArgsFlags() []string { // GetPtraceTraceeParentArgsOptions returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentArgsOptions() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return []string{} } if !ev.PTrace.Tracee.HasParent() { return []string{} @@ -10945,15 +10170,14 @@ func (ev *Event) GetPtraceTraceeParentArgsOptions() []string { // GetPtraceTraceeParentArgsScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentArgsScrubbed() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -10963,15 +10187,14 @@ func (ev *Event) GetPtraceTraceeParentArgsScrubbed() string { // GetPtraceTraceeParentArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentArgsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "ptrace" { - return zeroValue + return false } if ev.PTrace.Tracee == nil { - return zeroValue + return false } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return false } if !ev.PTrace.Tracee.HasParent() { return false @@ -10981,15 +10204,14 @@ func (ev *Event) GetPtraceTraceeParentArgsTruncated() bool { // GetPtraceTraceeParentArgv returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentArgv() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return []string{} } if !ev.PTrace.Tracee.HasParent() { return []string{} @@ -10999,15 +10221,14 @@ func (ev *Event) GetPtraceTraceeParentArgv() []string { // GetPtraceTraceeParentArgv0 returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentArgv0() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11017,15 +10238,14 @@ func (ev *Event) GetPtraceTraceeParentArgv0() string { // GetPtraceTraceeParentArgvScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentArgvScrubbed() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return []string{} } if !ev.PTrace.Tracee.HasParent() { return []string{} @@ -11035,15 +10255,14 @@ func (ev *Event) GetPtraceTraceeParentArgvScrubbed() []string { // GetPtraceTraceeParentCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentCapEffective() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.HasParent() { return uint64(0) @@ -11053,15 +10272,14 @@ func (ev *Event) GetPtraceTraceeParentCapEffective() uint64 { // GetPtraceTraceeParentCapPermitted returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentCapPermitted() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.HasParent() { return uint64(0) @@ -11071,15 +10289,14 @@ func (ev *Event) GetPtraceTraceeParentCapPermitted() uint64 { // GetPtraceTraceeParentComm returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentComm() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11089,15 +10306,14 @@ func (ev *Event) GetPtraceTraceeParentComm() string { // GetPtraceTraceeParentContainerId returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentContainerId() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11107,15 +10323,14 @@ func (ev *Event) GetPtraceTraceeParentContainerId() string { // GetPtraceTraceeParentCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentCreatedAt() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return 0 } if !ev.PTrace.Tracee.HasParent() { return 0 @@ -11125,15 +10340,14 @@ func (ev *Event) GetPtraceTraceeParentCreatedAt() int { // GetPtraceTraceeParentEgid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentEgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -11143,15 +10357,14 @@ func (ev *Event) GetPtraceTraceeParentEgid() uint32 { // GetPtraceTraceeParentEgroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentEgroup() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11161,15 +10374,14 @@ func (ev *Event) GetPtraceTraceeParentEgroup() string { // GetPtraceTraceeParentEnvp returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentEnvp() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return []string{} } if !ev.PTrace.Tracee.HasParent() { return []string{} @@ -11179,15 +10391,14 @@ func (ev *Event) GetPtraceTraceeParentEnvp() []string { // GetPtraceTraceeParentEnvs returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentEnvs() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return []string{} } if !ev.PTrace.Tracee.HasParent() { return []string{} @@ -11197,15 +10408,14 @@ func (ev *Event) GetPtraceTraceeParentEnvs() []string { // GetPtraceTraceeParentEnvsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentEnvsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "ptrace" { - return zeroValue + return false } if ev.PTrace.Tracee == nil { - return zeroValue + return false } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return false } if !ev.PTrace.Tracee.HasParent() { return false @@ -11215,15 +10425,14 @@ func (ev *Event) GetPtraceTraceeParentEnvsTruncated() bool { // GetPtraceTraceeParentEuid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentEuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -11233,15 +10442,14 @@ func (ev *Event) GetPtraceTraceeParentEuid() uint32 { // GetPtraceTraceeParentEuser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentEuser() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11251,15 +10459,14 @@ func (ev *Event) GetPtraceTraceeParentEuser() string { // GetPtraceTraceeParentFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.HasParent() { return uint64(0) @@ -11272,15 +10479,14 @@ func (ev *Event) GetPtraceTraceeParentFileChangeTime() uint64 { // GetPtraceTraceeParentFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11293,15 +10499,14 @@ func (ev *Event) GetPtraceTraceeParentFileFilesystem() string { // GetPtraceTraceeParentFileGid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -11314,15 +10519,14 @@ func (ev *Event) GetPtraceTraceeParentFileGid() uint32 { // GetPtraceTraceeParentFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11335,15 +10539,14 @@ func (ev *Event) GetPtraceTraceeParentFileGroup() string { // GetPtraceTraceeParentFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return []string{} } if !ev.PTrace.Tracee.HasParent() { return []string{} @@ -11356,15 +10559,14 @@ func (ev *Event) GetPtraceTraceeParentFileHashes() []string { // GetPtraceTraceeParentFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "ptrace" { - return zeroValue + return false } if ev.PTrace.Tracee == nil { - return zeroValue + return false } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return false } if !ev.PTrace.Tracee.HasParent() { return false @@ -11377,15 +10579,14 @@ func (ev *Event) GetPtraceTraceeParentFileInUpperLayer() bool { // GetPtraceTraceeParentFileInode returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.HasParent() { return uint64(0) @@ -11398,15 +10599,14 @@ func (ev *Event) GetPtraceTraceeParentFileInode() uint64 { // GetPtraceTraceeParentFileMode returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint16(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint16(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint16(0) } if !ev.PTrace.Tracee.HasParent() { return uint16(0) @@ -11419,15 +10619,14 @@ func (ev *Event) GetPtraceTraceeParentFileMode() uint16 { // GetPtraceTraceeParentFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.HasParent() { return uint64(0) @@ -11440,15 +10639,14 @@ func (ev *Event) GetPtraceTraceeParentFileModificationTime() uint64 { // GetPtraceTraceeParentFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -11461,15 +10659,14 @@ func (ev *Event) GetPtraceTraceeParentFileMountId() uint32 { // GetPtraceTraceeParentFileName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileName() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11482,30 +10679,28 @@ func (ev *Event) GetPtraceTraceeParentFileName() string { // GetPtraceTraceeParentFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.PTrace.Tracee.Parent.FileEvent)) } // GetPtraceTraceeParentFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11518,15 +10713,14 @@ func (ev *Event) GetPtraceTraceeParentFilePackageName() string { // GetPtraceTraceeParentFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11539,15 +10733,14 @@ func (ev *Event) GetPtraceTraceeParentFilePackageSourceVersion() string { // GetPtraceTraceeParentFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11560,15 +10753,14 @@ func (ev *Event) GetPtraceTraceeParentFilePackageVersion() string { // GetPtraceTraceeParentFilePath returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11581,30 +10773,28 @@ func (ev *Event) GetPtraceTraceeParentFilePath() string { // GetPtraceTraceeParentFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.PTrace.Tracee.Parent.FileEvent)) } // GetPtraceTraceeParentFileRights returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return 0 } if !ev.PTrace.Tracee.HasParent() { return 0 @@ -11617,15 +10807,14 @@ func (ev *Event) GetPtraceTraceeParentFileRights() int { // GetPtraceTraceeParentFileUid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -11638,15 +10827,14 @@ func (ev *Event) GetPtraceTraceeParentFileUid() uint32 { // GetPtraceTraceeParentFileUser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11659,15 +10847,14 @@ func (ev *Event) GetPtraceTraceeParentFileUser() string { // GetPtraceTraceeParentFsgid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFsgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -11677,15 +10864,14 @@ func (ev *Event) GetPtraceTraceeParentFsgid() uint32 { // GetPtraceTraceeParentFsgroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFsgroup() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11695,15 +10881,14 @@ func (ev *Event) GetPtraceTraceeParentFsgroup() string { // GetPtraceTraceeParentFsuid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFsuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -11713,15 +10898,14 @@ func (ev *Event) GetPtraceTraceeParentFsuid() uint32 { // GetPtraceTraceeParentFsuser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentFsuser() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11731,15 +10915,14 @@ func (ev *Event) GetPtraceTraceeParentFsuser() string { // GetPtraceTraceeParentGid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -11749,15 +10932,14 @@ func (ev *Event) GetPtraceTraceeParentGid() uint32 { // GetPtraceTraceeParentGroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentGroup() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11767,15 +10949,14 @@ func (ev *Event) GetPtraceTraceeParentGroup() string { // GetPtraceTraceeParentInterpreterFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.HasParent() { return uint64(0) @@ -11788,15 +10969,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileChangeTime() uint64 { // GetPtraceTraceeParentInterpreterFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11809,15 +10989,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileFilesystem() string { // GetPtraceTraceeParentInterpreterFileGid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -11830,15 +11009,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileGid() uint32 { // GetPtraceTraceeParentInterpreterFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11851,15 +11029,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileGroup() string { // GetPtraceTraceeParentInterpreterFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return []string{} } if !ev.PTrace.Tracee.HasParent() { return []string{} @@ -11872,15 +11049,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileHashes() []string { // GetPtraceTraceeParentInterpreterFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "ptrace" { - return zeroValue + return false } if ev.PTrace.Tracee == nil { - return zeroValue + return false } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return false } if !ev.PTrace.Tracee.HasParent() { return false @@ -11893,15 +11069,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileInUpperLayer() bool { // GetPtraceTraceeParentInterpreterFileInode returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.HasParent() { return uint64(0) @@ -11914,15 +11089,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileInode() uint64 { // GetPtraceTraceeParentInterpreterFileMode returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint16(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint16(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint16(0) } if !ev.PTrace.Tracee.HasParent() { return uint16(0) @@ -11935,15 +11109,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileMode() uint16 { // GetPtraceTraceeParentInterpreterFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint64(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint64(0) } if !ev.PTrace.Tracee.HasParent() { return uint64(0) @@ -11956,15 +11129,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileModificationTime() uint64 { // GetPtraceTraceeParentInterpreterFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -11977,15 +11149,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileMountId() uint32 { // GetPtraceTraceeParentInterpreterFileName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileName() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -11998,30 +11169,28 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileName() string { // GetPtraceTraceeParentInterpreterFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.PTrace.Tracee.Parent.LinuxBinprm.FileEvent)) } // GetPtraceTraceeParentInterpreterFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -12034,15 +11203,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFilePackageName() string { // GetPtraceTraceeParentInterpreterFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -12055,15 +11223,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFilePackageSourceVersion() stri // GetPtraceTraceeParentInterpreterFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -12076,15 +11243,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFilePackageVersion() string { // GetPtraceTraceeParentInterpreterFilePath returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -12097,30 +11263,28 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFilePath() string { // GetPtraceTraceeParentInterpreterFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.PTrace.Tracee.Parent.LinuxBinprm.FileEvent)) } // GetPtraceTraceeParentInterpreterFileRights returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "ptrace" { - return zeroValue + return 0 } if ev.PTrace.Tracee == nil { - return zeroValue + return 0 } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return 0 } if !ev.PTrace.Tracee.HasParent() { return 0 @@ -12133,15 +11297,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileRights() int { // GetPtraceTraceeParentInterpreterFileUid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -12154,15 +11317,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileUid() uint32 { // GetPtraceTraceeParentInterpreterFileUser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentInterpreterFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -12175,15 +11337,14 @@ func (ev *Event) GetPtraceTraceeParentInterpreterFileUser() string { // GetPtraceTraceeParentIsKworker returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentIsKworker() bool { - zeroValue := false if ev.GetEventType().String() != "ptrace" { - return zeroValue + return false } if ev.PTrace.Tracee == nil { - return zeroValue + return false } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return false } if !ev.PTrace.Tracee.HasParent() { return false @@ -12193,15 +11354,14 @@ func (ev *Event) GetPtraceTraceeParentIsKworker() bool { // GetPtraceTraceeParentIsThread returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentIsThread() bool { - zeroValue := false if ev.GetEventType().String() != "ptrace" { - return zeroValue + return false } if ev.PTrace.Tracee == nil { - return zeroValue + return false } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return false } if !ev.PTrace.Tracee.HasParent() { return false @@ -12211,15 +11371,14 @@ func (ev *Event) GetPtraceTraceeParentIsThread() bool { // GetPtraceTraceeParentPid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentPid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -12229,15 +11388,14 @@ func (ev *Event) GetPtraceTraceeParentPid() uint32 { // GetPtraceTraceeParentPpid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentPpid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -12247,15 +11405,14 @@ func (ev *Event) GetPtraceTraceeParentPpid() uint32 { // GetPtraceTraceeParentTid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentTid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -12265,15 +11422,14 @@ func (ev *Event) GetPtraceTraceeParentTid() uint32 { // GetPtraceTraceeParentTtyName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentTtyName() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -12283,15 +11439,14 @@ func (ev *Event) GetPtraceTraceeParentTtyName() string { // GetPtraceTraceeParentUid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return uint32(0) } if !ev.PTrace.Tracee.HasParent() { return uint32(0) @@ -12301,15 +11456,14 @@ func (ev *Event) GetPtraceTraceeParentUid() uint32 { // GetPtraceTraceeParentUser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentUser() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -12319,15 +11473,14 @@ func (ev *Event) GetPtraceTraceeParentUser() string { // GetPtraceTraceeParentUserSessionK8sGroups returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentUserSessionK8sGroups() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return []string{} } if !ev.PTrace.Tracee.HasParent() { return []string{} @@ -12337,15 +11490,14 @@ func (ev *Event) GetPtraceTraceeParentUserSessionK8sGroups() []string { // GetPtraceTraceeParentUserSessionK8sUid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentUserSessionK8sUid() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -12355,15 +11507,14 @@ func (ev *Event) GetPtraceTraceeParentUserSessionK8sUid() string { // GetPtraceTraceeParentUserSessionK8sUsername returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeParentUserSessionK8sUsername() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } if ev.PTrace.Tracee.Parent == nil { - return zeroValue + return "" } if !ev.PTrace.Tracee.HasParent() { return "" @@ -12373,1257 +11524,1121 @@ func (ev *Event) GetPtraceTraceeParentUserSessionK8sUsername() string { // GetPtraceTraceePid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceePid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } return ev.PTrace.Tracee.Process.PIDContext.Pid } // GetPtraceTraceePpid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceePpid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } return ev.PTrace.Tracee.Process.PPid } // GetPtraceTraceeTid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeTid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } return ev.PTrace.Tracee.Process.PIDContext.Tid } // GetPtraceTraceeTtyName returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeTtyName() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.PTrace.Tracee.Process.TTYName } // GetPtraceTraceeUid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "ptrace" { - return zeroValue + return uint32(0) } if ev.PTrace.Tracee == nil { - return zeroValue + return uint32(0) } return ev.PTrace.Tracee.Process.Credentials.UID } // GetPtraceTraceeUser returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeUser() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.PTrace.Tracee.Process.Credentials.User } // GetPtraceTraceeUserSessionK8sGroups returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeUserSessionK8sGroups() []string { - zeroValue := []string{} if ev.GetEventType().String() != "ptrace" { - return zeroValue + return []string{} } if ev.PTrace.Tracee == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.PTrace.Tracee.Process.UserSession) } // GetPtraceTraceeUserSessionK8sUid returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeUserSessionK8sUid() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveK8SUID(ev, &ev.PTrace.Tracee.Process.UserSession) } // GetPtraceTraceeUserSessionK8sUsername returns the value of the field, resolving if necessary func (ev *Event) GetPtraceTraceeUserSessionK8sUsername() string { - zeroValue := "" if ev.GetEventType().String() != "ptrace" { - return zeroValue + return "" } if ev.PTrace.Tracee == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveK8SUsername(ev, &ev.PTrace.Tracee.Process.UserSession) } // GetRemovexattrFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "removexattr" { - return zeroValue + return uint64(0) } return ev.RemoveXAttr.File.FileFields.CTime } // GetRemovexattrFileDestinationName returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileDestinationName() string { - zeroValue := "" if ev.GetEventType().String() != "removexattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveXAttrName(ev, &ev.RemoveXAttr) } // GetRemovexattrFileDestinationNamespace returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileDestinationNamespace() string { - zeroValue := "" if ev.GetEventType().String() != "removexattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveXAttrNamespace(ev, &ev.RemoveXAttr) } // GetRemovexattrFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "removexattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.RemoveXAttr.File) } // GetRemovexattrFileGid returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "removexattr" { - return zeroValue + return uint32(0) } return ev.RemoveXAttr.File.FileFields.GID } // GetRemovexattrFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "removexattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.RemoveXAttr.File.FileFields) } // GetRemovexattrFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "removexattr" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.RemoveXAttr.File) } // GetRemovexattrFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "removexattr" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.RemoveXAttr.File.FileFields) } // GetRemovexattrFileInode returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "removexattr" { - return zeroValue + return uint64(0) } return ev.RemoveXAttr.File.FileFields.PathKey.Inode } // GetRemovexattrFileMode returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "removexattr" { - return zeroValue + return uint16(0) } return ev.RemoveXAttr.File.FileFields.Mode } // GetRemovexattrFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "removexattr" { - return zeroValue + return uint64(0) } return ev.RemoveXAttr.File.FileFields.MTime } // GetRemovexattrFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "removexattr" { - return zeroValue + return uint32(0) } return ev.RemoveXAttr.File.FileFields.PathKey.MountID } // GetRemovexattrFileName returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileName() string { - zeroValue := "" if ev.GetEventType().String() != "removexattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.RemoveXAttr.File) } // GetRemovexattrFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "removexattr" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.RemoveXAttr.File)) } // GetRemovexattrFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "removexattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.RemoveXAttr.File) } // GetRemovexattrFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "removexattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.RemoveXAttr.File) } // GetRemovexattrFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "removexattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.RemoveXAttr.File) } // GetRemovexattrFilePath returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "removexattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.RemoveXAttr.File) } // GetRemovexattrFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "removexattr" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.RemoveXAttr.File)) } // GetRemovexattrFileRights returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "removexattr" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.RemoveXAttr.File.FileFields) } // GetRemovexattrFileUid returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "removexattr" { - return zeroValue + return uint32(0) } return ev.RemoveXAttr.File.FileFields.UID } // GetRemovexattrFileUser returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "removexattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.RemoveXAttr.File.FileFields) } // GetRemovexattrRetval returns the value of the field, resolving if necessary func (ev *Event) GetRemovexattrRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "removexattr" { - return zeroValue + return int64(0) } return ev.RemoveXAttr.SyscallEvent.Retval } // GetRenameFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint64(0) } return ev.Rename.Old.FileFields.CTime } // GetRenameFileDestinationChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint64(0) } return ev.Rename.New.FileFields.CTime } // GetRenameFileDestinationFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.Rename.New) } // GetRenameFileDestinationGid returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint32(0) } return ev.Rename.New.FileFields.GID } // GetRenameFileDestinationGroup returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationGroup() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Rename.New.FileFields) } // GetRenameFileDestinationHashes returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "rename" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Rename.New) } // GetRenameFileDestinationInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "rename" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.Rename.New.FileFields) } // GetRenameFileDestinationInode returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint64(0) } return ev.Rename.New.FileFields.PathKey.Inode } // GetRenameFileDestinationMode returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint16(0) } return ev.Rename.New.FileFields.Mode } // GetRenameFileDestinationModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint64(0) } return ev.Rename.New.FileFields.MTime } // GetRenameFileDestinationMountId returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint32(0) } return ev.Rename.New.FileFields.PathKey.MountID } // GetRenameFileDestinationName returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationName() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Rename.New) } // GetRenameFileDestinationNameLength returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "rename" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Rename.New)) } // GetRenameFileDestinationPackageName returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationPackageName() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.Rename.New) } // GetRenameFileDestinationPackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationPackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.Rename.New) } // GetRenameFileDestinationPackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationPackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.Rename.New) } // GetRenameFileDestinationPath returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationPath() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Rename.New) } // GetRenameFileDestinationPathLength returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationPathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "rename" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Rename.New)) } // GetRenameFileDestinationRights returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationRights() int { - zeroValue := 0 if ev.GetEventType().String() != "rename" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.Rename.New.FileFields) } // GetRenameFileDestinationUid returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint32(0) } return ev.Rename.New.FileFields.UID } // GetRenameFileDestinationUser returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileDestinationUser() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Rename.New.FileFields) } // GetRenameFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.Rename.Old) } // GetRenameFileGid returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint32(0) } return ev.Rename.Old.FileFields.GID } // GetRenameFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Rename.Old.FileFields) } // GetRenameFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "rename" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Rename.Old) } // GetRenameFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "rename" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.Rename.Old.FileFields) } // GetRenameFileInode returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint64(0) } return ev.Rename.Old.FileFields.PathKey.Inode } // GetRenameFileMode returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint16(0) } return ev.Rename.Old.FileFields.Mode } // GetRenameFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint64(0) } return ev.Rename.Old.FileFields.MTime } // GetRenameFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint32(0) } return ev.Rename.Old.FileFields.PathKey.MountID } // GetRenameFileName returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileName() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Rename.Old) } // GetRenameFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "rename" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Rename.Old)) } // GetRenameFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetRenameFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.Rename.Old) } // GetRenameFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetRenameFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.Rename.Old) } // GetRenameFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetRenameFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.Rename.Old) } // GetRenameFilePath returns the value of the field, resolving if necessary func (ev *Event) GetRenameFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Rename.Old) } // GetRenameFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetRenameFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "rename" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Rename.Old)) } // GetRenameFileRights returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "rename" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.Rename.Old.FileFields) } // GetRenameFileUid returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return uint32(0) } return ev.Rename.Old.FileFields.UID } // GetRenameFileUser returns the value of the field, resolving if necessary func (ev *Event) GetRenameFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "rename" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Rename.Old.FileFields) } // GetRenameRetval returns the value of the field, resolving if necessary func (ev *Event) GetRenameRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "rename" { - return zeroValue + return int64(0) } return ev.Rename.SyscallEvent.Retval } // GetRmdirFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "rmdir" { - return zeroValue + return uint64(0) } return ev.Rmdir.File.FileFields.CTime } // GetRmdirFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "rmdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.Rmdir.File) } // GetRmdirFileGid returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "rmdir" { - return zeroValue + return uint32(0) } return ev.Rmdir.File.FileFields.GID } // GetRmdirFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "rmdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Rmdir.File.FileFields) } // GetRmdirFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "rmdir" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Rmdir.File) } // GetRmdirFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "rmdir" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.Rmdir.File.FileFields) } // GetRmdirFileInode returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "rmdir" { - return zeroValue + return uint64(0) } return ev.Rmdir.File.FileFields.PathKey.Inode } // GetRmdirFileMode returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "rmdir" { - return zeroValue + return uint16(0) } return ev.Rmdir.File.FileFields.Mode } // GetRmdirFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "rmdir" { - return zeroValue + return uint64(0) } return ev.Rmdir.File.FileFields.MTime } // GetRmdirFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "rmdir" { - return zeroValue + return uint32(0) } return ev.Rmdir.File.FileFields.PathKey.MountID } // GetRmdirFileName returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileName() string { - zeroValue := "" if ev.GetEventType().String() != "rmdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Rmdir.File) } // GetRmdirFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "rmdir" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Rmdir.File)) } // GetRmdirFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "rmdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.Rmdir.File) } // GetRmdirFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "rmdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.Rmdir.File) } // GetRmdirFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "rmdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.Rmdir.File) } // GetRmdirFilePath returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "rmdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Rmdir.File) } // GetRmdirFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "rmdir" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Rmdir.File)) } // GetRmdirFileRights returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "rmdir" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.Rmdir.File.FileFields) } // GetRmdirFileUid returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "rmdir" { - return zeroValue + return uint32(0) } return ev.Rmdir.File.FileFields.UID } // GetRmdirFileUser returns the value of the field, resolving if necessary func (ev *Event) GetRmdirFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "rmdir" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Rmdir.File.FileFields) } // GetRmdirRetval returns the value of the field, resolving if necessary func (ev *Event) GetRmdirRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "rmdir" { - return zeroValue + return int64(0) } return ev.Rmdir.SyscallEvent.Retval } // GetSelinuxBoolName returns the value of the field, resolving if necessary func (ev *Event) GetSelinuxBoolName() string { - zeroValue := "" if ev.GetEventType().String() != "selinux" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveSELinuxBoolName(ev, &ev.SELinux) } // GetSelinuxBoolState returns the value of the field, resolving if necessary func (ev *Event) GetSelinuxBoolState() string { - zeroValue := "" if ev.GetEventType().String() != "selinux" { - return zeroValue + return "" } return ev.SELinux.BoolChangeValue } // GetSelinuxBoolCommitState returns the value of the field, resolving if necessary func (ev *Event) GetSelinuxBoolCommitState() bool { - zeroValue := false if ev.GetEventType().String() != "selinux" { - return zeroValue + return false } return ev.SELinux.BoolCommitValue } // GetSelinuxEnforceStatus returns the value of the field, resolving if necessary func (ev *Event) GetSelinuxEnforceStatus() string { - zeroValue := "" if ev.GetEventType().String() != "selinux" { - return zeroValue + return "" } return ev.SELinux.EnforceStatus } // GetSetgidEgid returns the value of the field, resolving if necessary func (ev *Event) GetSetgidEgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "setgid" { - return zeroValue + return uint32(0) } return ev.SetGID.EGID } // GetSetgidEgroup returns the value of the field, resolving if necessary func (ev *Event) GetSetgidEgroup() string { - zeroValue := "" if ev.GetEventType().String() != "setgid" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveSetgidEGroup(ev, &ev.SetGID) } // GetSetgidFsgid returns the value of the field, resolving if necessary func (ev *Event) GetSetgidFsgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "setgid" { - return zeroValue + return uint32(0) } return ev.SetGID.FSGID } // GetSetgidFsgroup returns the value of the field, resolving if necessary func (ev *Event) GetSetgidFsgroup() string { - zeroValue := "" if ev.GetEventType().String() != "setgid" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveSetgidFSGroup(ev, &ev.SetGID) } // GetSetgidGid returns the value of the field, resolving if necessary func (ev *Event) GetSetgidGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "setgid" { - return zeroValue + return uint32(0) } return ev.SetGID.GID } // GetSetgidGroup returns the value of the field, resolving if necessary func (ev *Event) GetSetgidGroup() string { - zeroValue := "" if ev.GetEventType().String() != "setgid" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveSetgidGroup(ev, &ev.SetGID) } // GetSetuidEuid returns the value of the field, resolving if necessary func (ev *Event) GetSetuidEuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "setuid" { - return zeroValue + return uint32(0) } return ev.SetUID.EUID } // GetSetuidEuser returns the value of the field, resolving if necessary func (ev *Event) GetSetuidEuser() string { - zeroValue := "" if ev.GetEventType().String() != "setuid" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveSetuidEUser(ev, &ev.SetUID) } // GetSetuidFsuid returns the value of the field, resolving if necessary func (ev *Event) GetSetuidFsuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "setuid" { - return zeroValue + return uint32(0) } return ev.SetUID.FSUID } // GetSetuidFsuser returns the value of the field, resolving if necessary func (ev *Event) GetSetuidFsuser() string { - zeroValue := "" if ev.GetEventType().String() != "setuid" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveSetuidFSUser(ev, &ev.SetUID) } // GetSetuidUid returns the value of the field, resolving if necessary func (ev *Event) GetSetuidUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "setuid" { - return zeroValue + return uint32(0) } return ev.SetUID.UID } // GetSetuidUser returns the value of the field, resolving if necessary func (ev *Event) GetSetuidUser() string { - zeroValue := "" if ev.GetEventType().String() != "setuid" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveSetuidUser(ev, &ev.SetUID) } // GetSetxattrFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "setxattr" { - return zeroValue + return uint64(0) } return ev.SetXAttr.File.FileFields.CTime } // GetSetxattrFileDestinationName returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileDestinationName() string { - zeroValue := "" if ev.GetEventType().String() != "setxattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveXAttrName(ev, &ev.SetXAttr) } // GetSetxattrFileDestinationNamespace returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileDestinationNamespace() string { - zeroValue := "" if ev.GetEventType().String() != "setxattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveXAttrNamespace(ev, &ev.SetXAttr) } // GetSetxattrFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "setxattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.SetXAttr.File) } // GetSetxattrFileGid returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "setxattr" { - return zeroValue + return uint32(0) } return ev.SetXAttr.File.FileFields.GID } // GetSetxattrFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "setxattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.SetXAttr.File.FileFields) } // GetSetxattrFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "setxattr" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.SetXAttr.File) } // GetSetxattrFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "setxattr" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.SetXAttr.File.FileFields) } // GetSetxattrFileInode returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "setxattr" { - return zeroValue + return uint64(0) } return ev.SetXAttr.File.FileFields.PathKey.Inode } // GetSetxattrFileMode returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "setxattr" { - return zeroValue + return uint16(0) } return ev.SetXAttr.File.FileFields.Mode } // GetSetxattrFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "setxattr" { - return zeroValue + return uint64(0) } return ev.SetXAttr.File.FileFields.MTime } // GetSetxattrFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "setxattr" { - return zeroValue + return uint32(0) } return ev.SetXAttr.File.FileFields.PathKey.MountID } // GetSetxattrFileName returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileName() string { - zeroValue := "" if ev.GetEventType().String() != "setxattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.SetXAttr.File) } // GetSetxattrFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "setxattr" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.SetXAttr.File)) } // GetSetxattrFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "setxattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.SetXAttr.File) } // GetSetxattrFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "setxattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.SetXAttr.File) } // GetSetxattrFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "setxattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.SetXAttr.File) } // GetSetxattrFilePath returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "setxattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.SetXAttr.File) } // GetSetxattrFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "setxattr" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.SetXAttr.File)) } // GetSetxattrFileRights returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "setxattr" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.SetXAttr.File.FileFields) } // GetSetxattrFileUid returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "setxattr" { - return zeroValue + return uint32(0) } return ev.SetXAttr.File.FileFields.UID } // GetSetxattrFileUser returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "setxattr" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.SetXAttr.File.FileFields) } // GetSetxattrRetval returns the value of the field, resolving if necessary func (ev *Event) GetSetxattrRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "setxattr" { - return zeroValue + return int64(0) } return ev.SetXAttr.SyscallEvent.Retval } // GetSignalPid returns the value of the field, resolving if necessary func (ev *Event) GetSignalPid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } return ev.Signal.PID } // GetSignalRetval returns the value of the field, resolving if necessary func (ev *Event) GetSignalRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return int64(0) } return ev.Signal.SyscallEvent.Retval } // GetSignalTargetAncestorsArgs returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsArgs() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -13640,15 +12655,14 @@ func (ev *Event) GetSignalTargetAncestorsArgs() []string { // GetSignalTargetAncestorsArgsFlags returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsArgsFlags() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -13665,15 +12679,14 @@ func (ev *Event) GetSignalTargetAncestorsArgsFlags() []string { // GetSignalTargetAncestorsArgsOptions returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsArgsOptions() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -13690,15 +12703,14 @@ func (ev *Event) GetSignalTargetAncestorsArgsOptions() []string { // GetSignalTargetAncestorsArgsScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsArgsScrubbed() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -13715,15 +12727,14 @@ func (ev *Event) GetSignalTargetAncestorsArgsScrubbed() []string { // GetSignalTargetAncestorsArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsArgsTruncated() []bool { - zeroValue := []bool{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []bool{} } if ev.Signal.Target == nil { - return zeroValue + return []bool{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -13740,15 +12751,14 @@ func (ev *Event) GetSignalTargetAncestorsArgsTruncated() []bool { // GetSignalTargetAncestorsArgv returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsArgv() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -13765,15 +12775,14 @@ func (ev *Event) GetSignalTargetAncestorsArgv() []string { // GetSignalTargetAncestorsArgv0 returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsArgv0() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -13790,15 +12799,14 @@ func (ev *Event) GetSignalTargetAncestorsArgv0() []string { // GetSignalTargetAncestorsArgvScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsArgvScrubbed() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -13815,15 +12823,14 @@ func (ev *Event) GetSignalTargetAncestorsArgvScrubbed() []string { // GetSignalTargetAncestorsCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsCapEffective() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint64{} } if ev.Signal.Target == nil { - return zeroValue + return []uint64{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -13840,15 +12847,14 @@ func (ev *Event) GetSignalTargetAncestorsCapEffective() []uint64 { // GetSignalTargetAncestorsCapPermitted returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsCapPermitted() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint64{} } if ev.Signal.Target == nil { - return zeroValue + return []uint64{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -13865,15 +12871,14 @@ func (ev *Event) GetSignalTargetAncestorsCapPermitted() []uint64 { // GetSignalTargetAncestorsComm returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsComm() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -13890,15 +12895,14 @@ func (ev *Event) GetSignalTargetAncestorsComm() []string { // GetSignalTargetAncestorsContainerId returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsContainerId() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -13915,15 +12919,14 @@ func (ev *Event) GetSignalTargetAncestorsContainerId() []string { // GetSignalTargetAncestorsCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsCreatedAt() []int { - zeroValue := []int{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []int{} } if ev.Signal.Target == nil { - return zeroValue + return []int{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -13940,15 +12943,14 @@ func (ev *Event) GetSignalTargetAncestorsCreatedAt() []int { // GetSignalTargetAncestorsEgid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsEgid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -13965,15 +12967,14 @@ func (ev *Event) GetSignalTargetAncestorsEgid() []uint32 { // GetSignalTargetAncestorsEgroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsEgroup() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -13990,15 +12991,14 @@ func (ev *Event) GetSignalTargetAncestorsEgroup() []string { // GetSignalTargetAncestorsEnvp returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsEnvp() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14015,15 +13015,14 @@ func (ev *Event) GetSignalTargetAncestorsEnvp() []string { // GetSignalTargetAncestorsEnvs returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsEnvs() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14040,15 +13039,14 @@ func (ev *Event) GetSignalTargetAncestorsEnvs() []string { // GetSignalTargetAncestorsEnvsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsEnvsTruncated() []bool { - zeroValue := []bool{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []bool{} } if ev.Signal.Target == nil { - return zeroValue + return []bool{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -14065,15 +13063,14 @@ func (ev *Event) GetSignalTargetAncestorsEnvsTruncated() []bool { // GetSignalTargetAncestorsEuid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsEuid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -14090,15 +13087,14 @@ func (ev *Event) GetSignalTargetAncestorsEuid() []uint32 { // GetSignalTargetAncestorsEuser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsEuser() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14115,15 +13111,14 @@ func (ev *Event) GetSignalTargetAncestorsEuser() []string { // GetSignalTargetAncestorsFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileChangeTime() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint64{} } if ev.Signal.Target == nil { - return zeroValue + return []uint64{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -14140,15 +13135,14 @@ func (ev *Event) GetSignalTargetAncestorsFileChangeTime() []uint64 { // GetSignalTargetAncestorsFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileFilesystem() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14165,15 +13159,14 @@ func (ev *Event) GetSignalTargetAncestorsFileFilesystem() []string { // GetSignalTargetAncestorsFileGid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileGid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -14190,15 +13183,14 @@ func (ev *Event) GetSignalTargetAncestorsFileGid() []uint32 { // GetSignalTargetAncestorsFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileGroup() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14215,15 +13207,14 @@ func (ev *Event) GetSignalTargetAncestorsFileGroup() []string { // GetSignalTargetAncestorsFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14240,15 +13231,14 @@ func (ev *Event) GetSignalTargetAncestorsFileHashes() []string { // GetSignalTargetAncestorsFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileInUpperLayer() []bool { - zeroValue := []bool{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []bool{} } if ev.Signal.Target == nil { - return zeroValue + return []bool{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -14265,15 +13255,14 @@ func (ev *Event) GetSignalTargetAncestorsFileInUpperLayer() []bool { // GetSignalTargetAncestorsFileInode returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileInode() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint64{} } if ev.Signal.Target == nil { - return zeroValue + return []uint64{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -14290,15 +13279,14 @@ func (ev *Event) GetSignalTargetAncestorsFileInode() []uint64 { // GetSignalTargetAncestorsFileMode returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileMode() []uint16 { - zeroValue := []uint16{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint16{} } if ev.Signal.Target == nil { - return zeroValue + return []uint16{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint16{} } var values []uint16 ctx := eval.NewContext(ev) @@ -14315,15 +13303,14 @@ func (ev *Event) GetSignalTargetAncestorsFileMode() []uint16 { // GetSignalTargetAncestorsFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileModificationTime() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint64{} } if ev.Signal.Target == nil { - return zeroValue + return []uint64{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -14340,15 +13327,14 @@ func (ev *Event) GetSignalTargetAncestorsFileModificationTime() []uint64 { // GetSignalTargetAncestorsFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileMountId() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -14365,15 +13351,14 @@ func (ev *Event) GetSignalTargetAncestorsFileMountId() []uint32 { // GetSignalTargetAncestorsFileName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileName() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14390,15 +13375,14 @@ func (ev *Event) GetSignalTargetAncestorsFileName() []string { // GetSignalTargetAncestorsFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileNameLength() []int { - zeroValue := []int{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []int{} } if ev.Signal.Target == nil { - return zeroValue + return []int{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -14415,15 +13399,14 @@ func (ev *Event) GetSignalTargetAncestorsFileNameLength() []int { // GetSignalTargetAncestorsFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFilePackageName() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14440,15 +13423,14 @@ func (ev *Event) GetSignalTargetAncestorsFilePackageName() []string { // GetSignalTargetAncestorsFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFilePackageSourceVersion() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14465,15 +13447,14 @@ func (ev *Event) GetSignalTargetAncestorsFilePackageSourceVersion() []string { // GetSignalTargetAncestorsFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFilePackageVersion() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14490,15 +13471,14 @@ func (ev *Event) GetSignalTargetAncestorsFilePackageVersion() []string { // GetSignalTargetAncestorsFilePath returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFilePath() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14515,15 +13495,14 @@ func (ev *Event) GetSignalTargetAncestorsFilePath() []string { // GetSignalTargetAncestorsFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFilePathLength() []int { - zeroValue := []int{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []int{} } if ev.Signal.Target == nil { - return zeroValue + return []int{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -14540,15 +13519,14 @@ func (ev *Event) GetSignalTargetAncestorsFilePathLength() []int { // GetSignalTargetAncestorsFileRights returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileRights() []int { - zeroValue := []int{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []int{} } if ev.Signal.Target == nil { - return zeroValue + return []int{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -14565,15 +13543,14 @@ func (ev *Event) GetSignalTargetAncestorsFileRights() []int { // GetSignalTargetAncestorsFileUid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileUid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -14590,15 +13567,14 @@ func (ev *Event) GetSignalTargetAncestorsFileUid() []uint32 { // GetSignalTargetAncestorsFileUser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFileUser() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14615,15 +13591,14 @@ func (ev *Event) GetSignalTargetAncestorsFileUser() []string { // GetSignalTargetAncestorsFsgid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFsgid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -14640,15 +13615,14 @@ func (ev *Event) GetSignalTargetAncestorsFsgid() []uint32 { // GetSignalTargetAncestorsFsgroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFsgroup() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14665,15 +13639,14 @@ func (ev *Event) GetSignalTargetAncestorsFsgroup() []string { // GetSignalTargetAncestorsFsuid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFsuid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -14690,15 +13663,14 @@ func (ev *Event) GetSignalTargetAncestorsFsuid() []uint32 { // GetSignalTargetAncestorsFsuser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsFsuser() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14715,15 +13687,14 @@ func (ev *Event) GetSignalTargetAncestorsFsuser() []string { // GetSignalTargetAncestorsGid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsGid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -14740,15 +13711,14 @@ func (ev *Event) GetSignalTargetAncestorsGid() []uint32 { // GetSignalTargetAncestorsGroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsGroup() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14765,15 +13735,14 @@ func (ev *Event) GetSignalTargetAncestorsGroup() []string { // GetSignalTargetAncestorsInterpreterFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileChangeTime() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint64{} } if ev.Signal.Target == nil { - return zeroValue + return []uint64{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -14790,15 +13759,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileChangeTime() []uint64 { // GetSignalTargetAncestorsInterpreterFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileFilesystem() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14815,15 +13783,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileFilesystem() []string { // GetSignalTargetAncestorsInterpreterFileGid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileGid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -14840,15 +13807,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileGid() []uint32 { // GetSignalTargetAncestorsInterpreterFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileGroup() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14865,15 +13831,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileGroup() []string { // GetSignalTargetAncestorsInterpreterFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -14890,15 +13855,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileHashes() []string { // GetSignalTargetAncestorsInterpreterFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileInUpperLayer() []bool { - zeroValue := []bool{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []bool{} } if ev.Signal.Target == nil { - return zeroValue + return []bool{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -14915,15 +13879,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileInUpperLayer() []bool { // GetSignalTargetAncestorsInterpreterFileInode returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileInode() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint64{} } if ev.Signal.Target == nil { - return zeroValue + return []uint64{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -14940,15 +13903,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileInode() []uint64 { // GetSignalTargetAncestorsInterpreterFileMode returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileMode() []uint16 { - zeroValue := []uint16{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint16{} } if ev.Signal.Target == nil { - return zeroValue + return []uint16{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint16{} } var values []uint16 ctx := eval.NewContext(ev) @@ -14965,15 +13927,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileMode() []uint16 { // GetSignalTargetAncestorsInterpreterFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileModificationTime() []uint64 { - zeroValue := []uint64{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint64{} } if ev.Signal.Target == nil { - return zeroValue + return []uint64{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint64{} } var values []uint64 ctx := eval.NewContext(ev) @@ -14990,15 +13951,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileModificationTime() []uin // GetSignalTargetAncestorsInterpreterFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileMountId() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -15015,15 +13975,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileMountId() []uint32 { // GetSignalTargetAncestorsInterpreterFileName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileName() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -15040,15 +13999,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileName() []string { // GetSignalTargetAncestorsInterpreterFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileNameLength() []int { - zeroValue := []int{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []int{} } if ev.Signal.Target == nil { - return zeroValue + return []int{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -15065,15 +14023,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileNameLength() []int { // GetSignalTargetAncestorsInterpreterFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFilePackageName() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -15090,15 +14047,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFilePackageName() []string { // GetSignalTargetAncestorsInterpreterFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFilePackageSourceVersion() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -15115,15 +14071,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFilePackageSourceVersion() [ // GetSignalTargetAncestorsInterpreterFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFilePackageVersion() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -15140,15 +14095,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFilePackageVersion() []strin // GetSignalTargetAncestorsInterpreterFilePath returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFilePath() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -15165,15 +14119,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFilePath() []string { // GetSignalTargetAncestorsInterpreterFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFilePathLength() []int { - zeroValue := []int{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []int{} } if ev.Signal.Target == nil { - return zeroValue + return []int{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -15190,15 +14143,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFilePathLength() []int { // GetSignalTargetAncestorsInterpreterFileRights returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileRights() []int { - zeroValue := []int{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []int{} } if ev.Signal.Target == nil { - return zeroValue + return []int{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -15215,15 +14167,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileRights() []int { // GetSignalTargetAncestorsInterpreterFileUid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileUid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -15240,15 +14191,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileUid() []uint32 { // GetSignalTargetAncestorsInterpreterFileUser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsInterpreterFileUser() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -15265,15 +14215,14 @@ func (ev *Event) GetSignalTargetAncestorsInterpreterFileUser() []string { // GetSignalTargetAncestorsIsKworker returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsIsKworker() []bool { - zeroValue := []bool{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []bool{} } if ev.Signal.Target == nil { - return zeroValue + return []bool{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -15290,15 +14239,14 @@ func (ev *Event) GetSignalTargetAncestorsIsKworker() []bool { // GetSignalTargetAncestorsIsThread returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsIsThread() []bool { - zeroValue := []bool{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []bool{} } if ev.Signal.Target == nil { - return zeroValue + return []bool{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []bool{} } var values []bool ctx := eval.NewContext(ev) @@ -15315,15 +14263,14 @@ func (ev *Event) GetSignalTargetAncestorsIsThread() []bool { // GetSignalTargetAncestorsPid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsPid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -15340,15 +14287,14 @@ func (ev *Event) GetSignalTargetAncestorsPid() []uint32 { // GetSignalTargetAncestorsPpid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsPpid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -15365,15 +14311,14 @@ func (ev *Event) GetSignalTargetAncestorsPpid() []uint32 { // GetSignalTargetAncestorsTid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsTid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -15390,15 +14335,14 @@ func (ev *Event) GetSignalTargetAncestorsTid() []uint32 { // GetSignalTargetAncestorsTtyName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsTtyName() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -15415,15 +14359,14 @@ func (ev *Event) GetSignalTargetAncestorsTtyName() []string { // GetSignalTargetAncestorsUid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsUid() []uint32 { - zeroValue := []uint32{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []uint32{} } if ev.Signal.Target == nil { - return zeroValue + return []uint32{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -15440,15 +14383,14 @@ func (ev *Event) GetSignalTargetAncestorsUid() []uint32 { // GetSignalTargetAncestorsUser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsUser() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -15465,15 +14407,14 @@ func (ev *Event) GetSignalTargetAncestorsUser() []string { // GetSignalTargetAncestorsUserSessionK8sGroups returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsUserSessionK8sGroups() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -15490,15 +14431,14 @@ func (ev *Event) GetSignalTargetAncestorsUserSessionK8sGroups() []string { // GetSignalTargetAncestorsUserSessionK8sUid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsUserSessionK8sUid() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -15515,15 +14455,14 @@ func (ev *Event) GetSignalTargetAncestorsUserSessionK8sUid() []string { // GetSignalTargetAncestorsUserSessionK8sUsername returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetAncestorsUserSessionK8sUsername() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -15540,276 +14479,253 @@ func (ev *Event) GetSignalTargetAncestorsUserSessionK8sUsername() []string { // GetSignalTargetArgs returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetArgs() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgs(ev, &ev.Signal.Target.Process) } // GetSignalTargetArgsFlags returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetArgsFlags() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgsFlags(ev, &ev.Signal.Target.Process) } // GetSignalTargetArgsOptions returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetArgsOptions() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgsOptions(ev, &ev.Signal.Target.Process) } // GetSignalTargetArgsScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetArgsScrubbed() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgsScrubbed(ev, &ev.Signal.Target.Process) } // GetSignalTargetArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetArgsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "signal" { - return zeroValue + return false } if ev.Signal.Target == nil { - return zeroValue + return false } return ev.FieldHandlers.ResolveProcessArgsTruncated(ev, &ev.Signal.Target.Process) } // GetSignalTargetArgv returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetArgv() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgv(ev, &ev.Signal.Target.Process) } // GetSignalTargetArgv0 returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetArgv0() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessArgv0(ev, &ev.Signal.Target.Process) } // GetSignalTargetArgvScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetArgvScrubbed() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessArgvScrubbed(ev, &ev.Signal.Target.Process) } // GetSignalTargetCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetCapEffective() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } return ev.Signal.Target.Process.Credentials.CapEffective } // GetSignalTargetCapPermitted returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetCapPermitted() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } return ev.Signal.Target.Process.Credentials.CapPermitted } // GetSignalTargetComm returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetComm() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.Signal.Target.Process.Comm } // GetSignalTargetContainerId returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetContainerId() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.Signal.Target.Process.ContainerID } // GetSignalTargetCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetCreatedAt() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveProcessCreatedAt(ev, &ev.Signal.Target.Process) } // GetSignalTargetEgid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetEgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } return ev.Signal.Target.Process.Credentials.EGID } // GetSignalTargetEgroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetEgroup() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.Signal.Target.Process.Credentials.EGroup } // GetSignalTargetEnvp returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetEnvp() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.Signal.Target.Process) } // GetSignalTargetEnvs returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetEnvs() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.Signal.Target.Process) } // GetSignalTargetEnvsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetEnvsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "signal" { - return zeroValue + return false } if ev.Signal.Target == nil { - return zeroValue + return false } return ev.FieldHandlers.ResolveProcessEnvsTruncated(ev, &ev.Signal.Target.Process) } // GetSignalTargetEuid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetEuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } return ev.Signal.Target.Process.Credentials.EUID } // GetSignalTargetEuser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetEuser() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.Signal.Target.Process.Credentials.EUser } // GetSignalTargetExecTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetExecTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "signal" { - return zeroValue + return time.Time{} } if ev.Signal.Target == nil { - return zeroValue + return time.Time{} } return ev.Signal.Target.Process.ExecTime } // GetSignalTargetExitTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetExitTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "signal" { - return zeroValue + return time.Time{} } if ev.Signal.Target == nil { - return zeroValue + return time.Time{} } return ev.Signal.Target.Process.ExitTime } // GetSignalTargetFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.Process.IsNotKworker() { return uint64(0) @@ -15819,12 +14735,11 @@ func (ev *Event) GetSignalTargetFileChangeTime() uint64 { // GetSignalTargetFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.IsNotKworker() { return "" @@ -15834,12 +14749,11 @@ func (ev *Event) GetSignalTargetFileFilesystem() string { // GetSignalTargetFileGid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.Process.IsNotKworker() { return uint32(0) @@ -15849,12 +14763,11 @@ func (ev *Event) GetSignalTargetFileGid() uint32 { // GetSignalTargetFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.IsNotKworker() { return "" @@ -15864,12 +14777,11 @@ func (ev *Event) GetSignalTargetFileGroup() string { // GetSignalTargetFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if !ev.Signal.Target.Process.IsNotKworker() { return []string{} @@ -15879,12 +14791,11 @@ func (ev *Event) GetSignalTargetFileHashes() []string { // GetSignalTargetFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "signal" { - return zeroValue + return false } if ev.Signal.Target == nil { - return zeroValue + return false } if !ev.Signal.Target.Process.IsNotKworker() { return false @@ -15894,12 +14805,11 @@ func (ev *Event) GetSignalTargetFileInUpperLayer() bool { // GetSignalTargetFileInode returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.Process.IsNotKworker() { return uint64(0) @@ -15909,12 +14819,11 @@ func (ev *Event) GetSignalTargetFileInode() uint64 { // GetSignalTargetFileMode returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint16(0) } if ev.Signal.Target == nil { - return zeroValue + return uint16(0) } if !ev.Signal.Target.Process.IsNotKworker() { return uint16(0) @@ -15924,12 +14833,11 @@ func (ev *Event) GetSignalTargetFileMode() uint16 { // GetSignalTargetFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.Process.IsNotKworker() { return uint64(0) @@ -15939,12 +14847,11 @@ func (ev *Event) GetSignalTargetFileModificationTime() uint64 { // GetSignalTargetFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.Process.IsNotKworker() { return uint32(0) @@ -15954,12 +14861,11 @@ func (ev *Event) GetSignalTargetFileMountId() uint32 { // GetSignalTargetFileName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileName() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.IsNotKworker() { return "" @@ -15969,24 +14875,22 @@ func (ev *Event) GetSignalTargetFileName() string { // GetSignalTargetFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Signal.Target.Process.FileEvent)) } // GetSignalTargetFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.IsNotKworker() { return "" @@ -15996,12 +14900,11 @@ func (ev *Event) GetSignalTargetFilePackageName() string { // GetSignalTargetFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.IsNotKworker() { return "" @@ -16011,12 +14914,11 @@ func (ev *Event) GetSignalTargetFilePackageSourceVersion() string { // GetSignalTargetFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.IsNotKworker() { return "" @@ -16026,12 +14928,11 @@ func (ev *Event) GetSignalTargetFilePackageVersion() string { // GetSignalTargetFilePath returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.IsNotKworker() { return "" @@ -16041,24 +14942,22 @@ func (ev *Event) GetSignalTargetFilePath() string { // GetSignalTargetFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Signal.Target.Process.FileEvent)) } // GetSignalTargetFileRights returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } if !ev.Signal.Target.Process.IsNotKworker() { return 0 @@ -16068,12 +14967,11 @@ func (ev *Event) GetSignalTargetFileRights() int { // GetSignalTargetFileUid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.Process.IsNotKworker() { return uint32(0) @@ -16083,12 +14981,11 @@ func (ev *Event) GetSignalTargetFileUid() uint32 { // GetSignalTargetFileUser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.IsNotKworker() { return "" @@ -16098,96 +14995,88 @@ func (ev *Event) GetSignalTargetFileUser() string { // GetSignalTargetForkTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetForkTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "signal" { - return zeroValue + return time.Time{} } if ev.Signal.Target == nil { - return zeroValue + return time.Time{} } return ev.Signal.Target.Process.ForkTime } // GetSignalTargetFsgid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFsgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } return ev.Signal.Target.Process.Credentials.FSGID } // GetSignalTargetFsgroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFsgroup() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.Signal.Target.Process.Credentials.FSGroup } // GetSignalTargetFsuid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFsuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } return ev.Signal.Target.Process.Credentials.FSUID } // GetSignalTargetFsuser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetFsuser() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.Signal.Target.Process.Credentials.FSUser } // GetSignalTargetGid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } return ev.Signal.Target.Process.Credentials.GID } // GetSignalTargetGroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetGroup() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.Signal.Target.Process.Credentials.Group } // GetSignalTargetInterpreterFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.Process.HasInterpreter() { return uint64(0) @@ -16197,12 +15086,11 @@ func (ev *Event) GetSignalTargetInterpreterFileChangeTime() uint64 { // GetSignalTargetInterpreterFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.HasInterpreter() { return "" @@ -16212,12 +15100,11 @@ func (ev *Event) GetSignalTargetInterpreterFileFilesystem() string { // GetSignalTargetInterpreterFileGid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.Process.HasInterpreter() { return uint32(0) @@ -16227,12 +15114,11 @@ func (ev *Event) GetSignalTargetInterpreterFileGid() uint32 { // GetSignalTargetInterpreterFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.HasInterpreter() { return "" @@ -16242,12 +15128,11 @@ func (ev *Event) GetSignalTargetInterpreterFileGroup() string { // GetSignalTargetInterpreterFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if !ev.Signal.Target.Process.HasInterpreter() { return []string{} @@ -16257,12 +15142,11 @@ func (ev *Event) GetSignalTargetInterpreterFileHashes() []string { // GetSignalTargetInterpreterFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "signal" { - return zeroValue + return false } if ev.Signal.Target == nil { - return zeroValue + return false } if !ev.Signal.Target.Process.HasInterpreter() { return false @@ -16272,12 +15156,11 @@ func (ev *Event) GetSignalTargetInterpreterFileInUpperLayer() bool { // GetSignalTargetInterpreterFileInode returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.Process.HasInterpreter() { return uint64(0) @@ -16287,12 +15170,11 @@ func (ev *Event) GetSignalTargetInterpreterFileInode() uint64 { // GetSignalTargetInterpreterFileMode returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint16(0) } if ev.Signal.Target == nil { - return zeroValue + return uint16(0) } if !ev.Signal.Target.Process.HasInterpreter() { return uint16(0) @@ -16302,12 +15184,11 @@ func (ev *Event) GetSignalTargetInterpreterFileMode() uint16 { // GetSignalTargetInterpreterFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.Process.HasInterpreter() { return uint64(0) @@ -16317,12 +15198,11 @@ func (ev *Event) GetSignalTargetInterpreterFileModificationTime() uint64 { // GetSignalTargetInterpreterFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.Process.HasInterpreter() { return uint32(0) @@ -16332,12 +15212,11 @@ func (ev *Event) GetSignalTargetInterpreterFileMountId() uint32 { // GetSignalTargetInterpreterFileName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileName() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.HasInterpreter() { return "" @@ -16347,24 +15226,22 @@ func (ev *Event) GetSignalTargetInterpreterFileName() string { // GetSignalTargetInterpreterFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Signal.Target.Process.LinuxBinprm.FileEvent)) } // GetSignalTargetInterpreterFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.HasInterpreter() { return "" @@ -16374,12 +15251,11 @@ func (ev *Event) GetSignalTargetInterpreterFilePackageName() string { // GetSignalTargetInterpreterFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.HasInterpreter() { return "" @@ -16389,12 +15265,11 @@ func (ev *Event) GetSignalTargetInterpreterFilePackageSourceVersion() string { // GetSignalTargetInterpreterFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.HasInterpreter() { return "" @@ -16404,12 +15279,11 @@ func (ev *Event) GetSignalTargetInterpreterFilePackageVersion() string { // GetSignalTargetInterpreterFilePath returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.HasInterpreter() { return "" @@ -16419,24 +15293,22 @@ func (ev *Event) GetSignalTargetInterpreterFilePath() string { // GetSignalTargetInterpreterFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Signal.Target.Process.LinuxBinprm.FileEvent)) } // GetSignalTargetInterpreterFileRights returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } if !ev.Signal.Target.Process.HasInterpreter() { return 0 @@ -16446,12 +15318,11 @@ func (ev *Event) GetSignalTargetInterpreterFileRights() int { // GetSignalTargetInterpreterFileUid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.Process.HasInterpreter() { return uint32(0) @@ -16461,12 +15332,11 @@ func (ev *Event) GetSignalTargetInterpreterFileUid() uint32 { // GetSignalTargetInterpreterFileUser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetInterpreterFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if !ev.Signal.Target.Process.HasInterpreter() { return "" @@ -16476,39 +15346,36 @@ func (ev *Event) GetSignalTargetInterpreterFileUser() string { // GetSignalTargetIsKworker returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetIsKworker() bool { - zeroValue := false if ev.GetEventType().String() != "signal" { - return zeroValue + return false } if ev.Signal.Target == nil { - return zeroValue + return false } return ev.Signal.Target.Process.PIDContext.IsKworker } // GetSignalTargetIsThread returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetIsThread() bool { - zeroValue := false if ev.GetEventType().String() != "signal" { - return zeroValue + return false } if ev.Signal.Target == nil { - return zeroValue + return false } return ev.Signal.Target.Process.IsThread } // GetSignalTargetParentArgs returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentArgs() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -16518,15 +15385,14 @@ func (ev *Event) GetSignalTargetParentArgs() string { // GetSignalTargetParentArgsFlags returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentArgsFlags() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Parent == nil { - return zeroValue + return []string{} } if !ev.Signal.Target.HasParent() { return []string{} @@ -16536,15 +15402,14 @@ func (ev *Event) GetSignalTargetParentArgsFlags() []string { // GetSignalTargetParentArgsOptions returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentArgsOptions() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Parent == nil { - return zeroValue + return []string{} } if !ev.Signal.Target.HasParent() { return []string{} @@ -16554,15 +15419,14 @@ func (ev *Event) GetSignalTargetParentArgsOptions() []string { // GetSignalTargetParentArgsScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentArgsScrubbed() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -16572,15 +15436,14 @@ func (ev *Event) GetSignalTargetParentArgsScrubbed() string { // GetSignalTargetParentArgsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentArgsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "signal" { - return zeroValue + return false } if ev.Signal.Target == nil { - return zeroValue + return false } if ev.Signal.Target.Parent == nil { - return zeroValue + return false } if !ev.Signal.Target.HasParent() { return false @@ -16590,15 +15453,14 @@ func (ev *Event) GetSignalTargetParentArgsTruncated() bool { // GetSignalTargetParentArgv returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentArgv() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Parent == nil { - return zeroValue + return []string{} } if !ev.Signal.Target.HasParent() { return []string{} @@ -16608,15 +15470,14 @@ func (ev *Event) GetSignalTargetParentArgv() []string { // GetSignalTargetParentArgv0 returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentArgv0() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -16626,15 +15487,14 @@ func (ev *Event) GetSignalTargetParentArgv0() string { // GetSignalTargetParentArgvScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentArgvScrubbed() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Parent == nil { - return zeroValue + return []string{} } if !ev.Signal.Target.HasParent() { return []string{} @@ -16644,15 +15504,14 @@ func (ev *Event) GetSignalTargetParentArgvScrubbed() []string { // GetSignalTargetParentCapEffective returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentCapEffective() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.HasParent() { return uint64(0) @@ -16662,15 +15521,14 @@ func (ev *Event) GetSignalTargetParentCapEffective() uint64 { // GetSignalTargetParentCapPermitted returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentCapPermitted() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.HasParent() { return uint64(0) @@ -16680,15 +15538,14 @@ func (ev *Event) GetSignalTargetParentCapPermitted() uint64 { // GetSignalTargetParentComm returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentComm() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -16698,15 +15555,14 @@ func (ev *Event) GetSignalTargetParentComm() string { // GetSignalTargetParentContainerId returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentContainerId() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -16716,15 +15572,14 @@ func (ev *Event) GetSignalTargetParentContainerId() string { // GetSignalTargetParentCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentCreatedAt() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } if ev.Signal.Target.Parent == nil { - return zeroValue + return 0 } if !ev.Signal.Target.HasParent() { return 0 @@ -16734,15 +15589,14 @@ func (ev *Event) GetSignalTargetParentCreatedAt() int { // GetSignalTargetParentEgid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentEgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -16752,15 +15606,14 @@ func (ev *Event) GetSignalTargetParentEgid() uint32 { // GetSignalTargetParentEgroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentEgroup() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -16770,15 +15623,14 @@ func (ev *Event) GetSignalTargetParentEgroup() string { // GetSignalTargetParentEnvp returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentEnvp() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Parent == nil { - return zeroValue + return []string{} } if !ev.Signal.Target.HasParent() { return []string{} @@ -16788,15 +15640,14 @@ func (ev *Event) GetSignalTargetParentEnvp() []string { // GetSignalTargetParentEnvs returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentEnvs() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Parent == nil { - return zeroValue + return []string{} } if !ev.Signal.Target.HasParent() { return []string{} @@ -16806,15 +15657,14 @@ func (ev *Event) GetSignalTargetParentEnvs() []string { // GetSignalTargetParentEnvsTruncated returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentEnvsTruncated() bool { - zeroValue := false if ev.GetEventType().String() != "signal" { - return zeroValue + return false } if ev.Signal.Target == nil { - return zeroValue + return false } if ev.Signal.Target.Parent == nil { - return zeroValue + return false } if !ev.Signal.Target.HasParent() { return false @@ -16824,15 +15674,14 @@ func (ev *Event) GetSignalTargetParentEnvsTruncated() bool { // GetSignalTargetParentEuid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentEuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -16842,15 +15691,14 @@ func (ev *Event) GetSignalTargetParentEuid() uint32 { // GetSignalTargetParentEuser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentEuser() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -16860,15 +15708,14 @@ func (ev *Event) GetSignalTargetParentEuser() string { // GetSignalTargetParentFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.HasParent() { return uint64(0) @@ -16881,15 +15728,14 @@ func (ev *Event) GetSignalTargetParentFileChangeTime() uint64 { // GetSignalTargetParentFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -16902,15 +15748,14 @@ func (ev *Event) GetSignalTargetParentFileFilesystem() string { // GetSignalTargetParentFileGid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -16923,15 +15768,14 @@ func (ev *Event) GetSignalTargetParentFileGid() uint32 { // GetSignalTargetParentFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -16944,15 +15788,14 @@ func (ev *Event) GetSignalTargetParentFileGroup() string { // GetSignalTargetParentFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Parent == nil { - return zeroValue + return []string{} } if !ev.Signal.Target.HasParent() { return []string{} @@ -16965,15 +15808,14 @@ func (ev *Event) GetSignalTargetParentFileHashes() []string { // GetSignalTargetParentFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "signal" { - return zeroValue + return false } if ev.Signal.Target == nil { - return zeroValue + return false } if ev.Signal.Target.Parent == nil { - return zeroValue + return false } if !ev.Signal.Target.HasParent() { return false @@ -16986,15 +15828,14 @@ func (ev *Event) GetSignalTargetParentFileInUpperLayer() bool { // GetSignalTargetParentFileInode returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.HasParent() { return uint64(0) @@ -17007,15 +15848,14 @@ func (ev *Event) GetSignalTargetParentFileInode() uint64 { // GetSignalTargetParentFileMode returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint16(0) } if ev.Signal.Target == nil { - return zeroValue + return uint16(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint16(0) } if !ev.Signal.Target.HasParent() { return uint16(0) @@ -17028,15 +15868,14 @@ func (ev *Event) GetSignalTargetParentFileMode() uint16 { // GetSignalTargetParentFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.HasParent() { return uint64(0) @@ -17049,15 +15888,14 @@ func (ev *Event) GetSignalTargetParentFileModificationTime() uint64 { // GetSignalTargetParentFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -17070,15 +15908,14 @@ func (ev *Event) GetSignalTargetParentFileMountId() uint32 { // GetSignalTargetParentFileName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileName() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17091,30 +15928,28 @@ func (ev *Event) GetSignalTargetParentFileName() string { // GetSignalTargetParentFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } if ev.Signal.Target.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Signal.Target.Parent.FileEvent)) } // GetSignalTargetParentFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17127,15 +15962,14 @@ func (ev *Event) GetSignalTargetParentFilePackageName() string { // GetSignalTargetParentFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17148,15 +15982,14 @@ func (ev *Event) GetSignalTargetParentFilePackageSourceVersion() string { // GetSignalTargetParentFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17169,15 +16002,14 @@ func (ev *Event) GetSignalTargetParentFilePackageVersion() string { // GetSignalTargetParentFilePath returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17190,30 +16022,28 @@ func (ev *Event) GetSignalTargetParentFilePath() string { // GetSignalTargetParentFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } if ev.Signal.Target.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Signal.Target.Parent.FileEvent)) } // GetSignalTargetParentFileRights returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } if ev.Signal.Target.Parent == nil { - return zeroValue + return 0 } if !ev.Signal.Target.HasParent() { return 0 @@ -17226,15 +16056,14 @@ func (ev *Event) GetSignalTargetParentFileRights() int { // GetSignalTargetParentFileUid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -17247,15 +16076,14 @@ func (ev *Event) GetSignalTargetParentFileUid() uint32 { // GetSignalTargetParentFileUser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17268,15 +16096,14 @@ func (ev *Event) GetSignalTargetParentFileUser() string { // GetSignalTargetParentFsgid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFsgid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -17286,15 +16113,14 @@ func (ev *Event) GetSignalTargetParentFsgid() uint32 { // GetSignalTargetParentFsgroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFsgroup() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17304,15 +16130,14 @@ func (ev *Event) GetSignalTargetParentFsgroup() string { // GetSignalTargetParentFsuid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFsuid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -17322,15 +16147,14 @@ func (ev *Event) GetSignalTargetParentFsuid() uint32 { // GetSignalTargetParentFsuser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentFsuser() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17340,15 +16164,14 @@ func (ev *Event) GetSignalTargetParentFsuser() string { // GetSignalTargetParentGid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -17358,15 +16181,14 @@ func (ev *Event) GetSignalTargetParentGid() uint32 { // GetSignalTargetParentGroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentGroup() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17376,15 +16198,14 @@ func (ev *Event) GetSignalTargetParentGroup() string { // GetSignalTargetParentInterpreterFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.HasParent() { return uint64(0) @@ -17397,15 +16218,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileChangeTime() uint64 { // GetSignalTargetParentInterpreterFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17418,15 +16238,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileFilesystem() string { // GetSignalTargetParentInterpreterFileGid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -17439,15 +16258,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileGid() uint32 { // GetSignalTargetParentInterpreterFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17460,15 +16278,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileGroup() string { // GetSignalTargetParentInterpreterFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Parent == nil { - return zeroValue + return []string{} } if !ev.Signal.Target.HasParent() { return []string{} @@ -17481,15 +16298,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileHashes() []string { // GetSignalTargetParentInterpreterFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "signal" { - return zeroValue + return false } if ev.Signal.Target == nil { - return zeroValue + return false } if ev.Signal.Target.Parent == nil { - return zeroValue + return false } if !ev.Signal.Target.HasParent() { return false @@ -17502,15 +16318,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileInUpperLayer() bool { // GetSignalTargetParentInterpreterFileInode returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.HasParent() { return uint64(0) @@ -17523,15 +16338,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileInode() uint64 { // GetSignalTargetParentInterpreterFileMode returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint16(0) } if ev.Signal.Target == nil { - return zeroValue + return uint16(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint16(0) } if !ev.Signal.Target.HasParent() { return uint16(0) @@ -17544,15 +16358,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileMode() uint16 { // GetSignalTargetParentInterpreterFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint64(0) } if ev.Signal.Target == nil { - return zeroValue + return uint64(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint64(0) } if !ev.Signal.Target.HasParent() { return uint64(0) @@ -17565,15 +16378,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileModificationTime() uint64 { // GetSignalTargetParentInterpreterFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -17586,15 +16398,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileMountId() uint32 { // GetSignalTargetParentInterpreterFileName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileName() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17607,30 +16418,28 @@ func (ev *Event) GetSignalTargetParentInterpreterFileName() string { // GetSignalTargetParentInterpreterFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } if ev.Signal.Target.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Signal.Target.Parent.LinuxBinprm.FileEvent)) } // GetSignalTargetParentInterpreterFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17643,15 +16452,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFilePackageName() string { // GetSignalTargetParentInterpreterFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17664,15 +16472,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFilePackageSourceVersion() stri // GetSignalTargetParentInterpreterFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17685,15 +16492,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFilePackageVersion() string { // GetSignalTargetParentInterpreterFilePath returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17706,30 +16512,28 @@ func (ev *Event) GetSignalTargetParentInterpreterFilePath() string { // GetSignalTargetParentInterpreterFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } if ev.Signal.Target.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Signal.Target.Parent.LinuxBinprm.FileEvent)) } // GetSignalTargetParentInterpreterFileRights returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "signal" { - return zeroValue + return 0 } if ev.Signal.Target == nil { - return zeroValue + return 0 } if ev.Signal.Target.Parent == nil { - return zeroValue + return 0 } if !ev.Signal.Target.HasParent() { return 0 @@ -17742,15 +16546,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileRights() int { // GetSignalTargetParentInterpreterFileUid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -17763,15 +16566,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileUid() uint32 { // GetSignalTargetParentInterpreterFileUser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentInterpreterFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17784,15 +16586,14 @@ func (ev *Event) GetSignalTargetParentInterpreterFileUser() string { // GetSignalTargetParentIsKworker returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentIsKworker() bool { - zeroValue := false if ev.GetEventType().String() != "signal" { - return zeroValue + return false } if ev.Signal.Target == nil { - return zeroValue + return false } if ev.Signal.Target.Parent == nil { - return zeroValue + return false } if !ev.Signal.Target.HasParent() { return false @@ -17802,15 +16603,14 @@ func (ev *Event) GetSignalTargetParentIsKworker() bool { // GetSignalTargetParentIsThread returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentIsThread() bool { - zeroValue := false if ev.GetEventType().String() != "signal" { - return zeroValue + return false } if ev.Signal.Target == nil { - return zeroValue + return false } if ev.Signal.Target.Parent == nil { - return zeroValue + return false } if !ev.Signal.Target.HasParent() { return false @@ -17820,15 +16620,14 @@ func (ev *Event) GetSignalTargetParentIsThread() bool { // GetSignalTargetParentPid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentPid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -17838,15 +16637,14 @@ func (ev *Event) GetSignalTargetParentPid() uint32 { // GetSignalTargetParentPpid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentPpid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -17856,15 +16654,14 @@ func (ev *Event) GetSignalTargetParentPpid() uint32 { // GetSignalTargetParentTid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentTid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -17874,15 +16671,14 @@ func (ev *Event) GetSignalTargetParentTid() uint32 { // GetSignalTargetParentTtyName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentTtyName() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17892,15 +16688,14 @@ func (ev *Event) GetSignalTargetParentTtyName() string { // GetSignalTargetParentUid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } if ev.Signal.Target.Parent == nil { - return zeroValue + return uint32(0) } if !ev.Signal.Target.HasParent() { return uint32(0) @@ -17910,15 +16705,14 @@ func (ev *Event) GetSignalTargetParentUid() uint32 { // GetSignalTargetParentUser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentUser() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17928,15 +16722,14 @@ func (ev *Event) GetSignalTargetParentUser() string { // GetSignalTargetParentUserSessionK8sGroups returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentUserSessionK8sGroups() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } if ev.Signal.Target.Parent == nil { - return zeroValue + return []string{} } if !ev.Signal.Target.HasParent() { return []string{} @@ -17946,15 +16739,14 @@ func (ev *Event) GetSignalTargetParentUserSessionK8sGroups() []string { // GetSignalTargetParentUserSessionK8sUid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentUserSessionK8sUid() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17964,15 +16756,14 @@ func (ev *Event) GetSignalTargetParentUserSessionK8sUid() string { // GetSignalTargetParentUserSessionK8sUsername returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetParentUserSessionK8sUsername() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } if ev.Signal.Target.Parent == nil { - return zeroValue + return "" } if !ev.Signal.Target.HasParent() { return "" @@ -17982,324 +16773,291 @@ func (ev *Event) GetSignalTargetParentUserSessionK8sUsername() string { // GetSignalTargetPid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetPid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } return ev.Signal.Target.Process.PIDContext.Pid } // GetSignalTargetPpid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetPpid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } return ev.Signal.Target.Process.PPid } // GetSignalTargetTid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetTid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } return ev.Signal.Target.Process.PIDContext.Tid } // GetSignalTargetTtyName returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetTtyName() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.Signal.Target.Process.TTYName } // GetSignalTargetUid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } if ev.Signal.Target == nil { - return zeroValue + return uint32(0) } return ev.Signal.Target.Process.Credentials.UID } // GetSignalTargetUser returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetUser() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.Signal.Target.Process.Credentials.User } // GetSignalTargetUserSessionK8sGroups returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetUserSessionK8sGroups() []string { - zeroValue := []string{} if ev.GetEventType().String() != "signal" { - return zeroValue + return []string{} } if ev.Signal.Target == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveK8SGroups(ev, &ev.Signal.Target.Process.UserSession) } // GetSignalTargetUserSessionK8sUid returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetUserSessionK8sUid() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveK8SUID(ev, &ev.Signal.Target.Process.UserSession) } // GetSignalTargetUserSessionK8sUsername returns the value of the field, resolving if necessary func (ev *Event) GetSignalTargetUserSessionK8sUsername() string { - zeroValue := "" if ev.GetEventType().String() != "signal" { - return zeroValue + return "" } if ev.Signal.Target == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveK8SUsername(ev, &ev.Signal.Target.Process.UserSession) } // GetSignalType returns the value of the field, resolving if necessary func (ev *Event) GetSignalType() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "signal" { - return zeroValue + return uint32(0) } return ev.Signal.Type } // GetSpliceFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "splice" { - return zeroValue + return uint64(0) } return ev.Splice.File.FileFields.CTime } // GetSpliceFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "splice" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.Splice.File) } // GetSpliceFileGid returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "splice" { - return zeroValue + return uint32(0) } return ev.Splice.File.FileFields.GID } // GetSpliceFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "splice" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Splice.File.FileFields) } // GetSpliceFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "splice" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Splice.File) } // GetSpliceFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "splice" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.Splice.File.FileFields) } // GetSpliceFileInode returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "splice" { - return zeroValue + return uint64(0) } return ev.Splice.File.FileFields.PathKey.Inode } // GetSpliceFileMode returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "splice" { - return zeroValue + return uint16(0) } return ev.Splice.File.FileFields.Mode } // GetSpliceFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "splice" { - return zeroValue + return uint64(0) } return ev.Splice.File.FileFields.MTime } // GetSpliceFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "splice" { - return zeroValue + return uint32(0) } return ev.Splice.File.FileFields.PathKey.MountID } // GetSpliceFileName returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileName() string { - zeroValue := "" if ev.GetEventType().String() != "splice" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Splice.File) } // GetSpliceFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "splice" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Splice.File)) } // GetSpliceFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "splice" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.Splice.File) } // GetSpliceFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "splice" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.Splice.File) } // GetSpliceFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "splice" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.Splice.File) } // GetSpliceFilePath returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "splice" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Splice.File) } // GetSpliceFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "splice" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Splice.File)) } // GetSpliceFileRights returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "splice" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.Splice.File.FileFields) } // GetSpliceFileUid returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "splice" { - return zeroValue + return uint32(0) } return ev.Splice.File.FileFields.UID } // GetSpliceFileUser returns the value of the field, resolving if necessary func (ev *Event) GetSpliceFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "splice" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Splice.File.FileFields) } // GetSplicePipeEntryFlag returns the value of the field, resolving if necessary func (ev *Event) GetSplicePipeEntryFlag() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "splice" { - return zeroValue + return uint32(0) } return ev.Splice.PipeEntryFlag } // GetSplicePipeExitFlag returns the value of the field, resolving if necessary func (ev *Event) GetSplicePipeExitFlag() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "splice" { - return zeroValue + return uint32(0) } return ev.Splice.PipeExitFlag } // GetSpliceRetval returns the value of the field, resolving if necessary func (ev *Event) GetSpliceRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "splice" { - return zeroValue + return int64(0) } return ev.Splice.SyscallEvent.Retval } @@ -18311,405 +17069,360 @@ func (ev *Event) GetTimestamp() time.Time { // GetUnlinkFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "unlink" { - return zeroValue + return uint64(0) } return ev.Unlink.File.FileFields.CTime } // GetUnlinkFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "unlink" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.Unlink.File) } // GetUnlinkFileGid returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "unlink" { - return zeroValue + return uint32(0) } return ev.Unlink.File.FileFields.GID } // GetUnlinkFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "unlink" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Unlink.File.FileFields) } // GetUnlinkFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "unlink" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Unlink.File) } // GetUnlinkFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "unlink" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.Unlink.File.FileFields) } // GetUnlinkFileInode returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "unlink" { - return zeroValue + return uint64(0) } return ev.Unlink.File.FileFields.PathKey.Inode } // GetUnlinkFileMode returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "unlink" { - return zeroValue + return uint16(0) } return ev.Unlink.File.FileFields.Mode } // GetUnlinkFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "unlink" { - return zeroValue + return uint64(0) } return ev.Unlink.File.FileFields.MTime } // GetUnlinkFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "unlink" { - return zeroValue + return uint32(0) } return ev.Unlink.File.FileFields.PathKey.MountID } // GetUnlinkFileName returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileName() string { - zeroValue := "" if ev.GetEventType().String() != "unlink" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Unlink.File) } // GetUnlinkFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "unlink" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Unlink.File)) } // GetUnlinkFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "unlink" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.Unlink.File) } // GetUnlinkFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "unlink" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.Unlink.File) } // GetUnlinkFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "unlink" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.Unlink.File) } // GetUnlinkFilePath returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "unlink" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Unlink.File) } // GetUnlinkFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "unlink" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Unlink.File)) } // GetUnlinkFileRights returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "unlink" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.Unlink.File.FileFields) } // GetUnlinkFileUid returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "unlink" { - return zeroValue + return uint32(0) } return ev.Unlink.File.FileFields.UID } // GetUnlinkFileUser returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "unlink" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Unlink.File.FileFields) } // GetUnlinkFlags returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkFlags() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "unlink" { - return zeroValue + return uint32(0) } return ev.Unlink.Flags } // GetUnlinkRetval returns the value of the field, resolving if necessary func (ev *Event) GetUnlinkRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "unlink" { - return zeroValue + return int64(0) } return ev.Unlink.SyscallEvent.Retval } // GetUnloadModuleName returns the value of the field, resolving if necessary func (ev *Event) GetUnloadModuleName() string { - zeroValue := "" if ev.GetEventType().String() != "unload_module" { - return zeroValue + return "" } return ev.UnloadModule.Name } // GetUnloadModuleRetval returns the value of the field, resolving if necessary func (ev *Event) GetUnloadModuleRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "unload_module" { - return zeroValue + return int64(0) } return ev.UnloadModule.SyscallEvent.Retval } // GetUtimesFileChangeTime returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileChangeTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "utimes" { - return zeroValue + return uint64(0) } return ev.Utimes.File.FileFields.CTime } // GetUtimesFileFilesystem returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileFilesystem() string { - zeroValue := "" if ev.GetEventType().String() != "utimes" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFilesystem(ev, &ev.Utimes.File) } // GetUtimesFileGid returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileGid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "utimes" { - return zeroValue + return uint32(0) } return ev.Utimes.File.FileFields.GID } // GetUtimesFileGroup returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileGroup() string { - zeroValue := "" if ev.GetEventType().String() != "utimes" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsGroup(ev, &ev.Utimes.File.FileFields) } // GetUtimesFileHashes returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileHashes() []string { - zeroValue := []string{} if ev.GetEventType().String() != "utimes" { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveHashesFromEvent(ev, &ev.Utimes.File) } // GetUtimesFileInUpperLayer returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileInUpperLayer() bool { - zeroValue := false if ev.GetEventType().String() != "utimes" { - return zeroValue + return false } return ev.FieldHandlers.ResolveFileFieldsInUpperLayer(ev, &ev.Utimes.File.FileFields) } // GetUtimesFileInode returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileInode() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "utimes" { - return zeroValue + return uint64(0) } return ev.Utimes.File.FileFields.PathKey.Inode } // GetUtimesFileMode returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileMode() uint16 { - zeroValue := uint16(0) if ev.GetEventType().String() != "utimes" { - return zeroValue + return uint16(0) } return ev.Utimes.File.FileFields.Mode } // GetUtimesFileModificationTime returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileModificationTime() uint64 { - zeroValue := uint64(0) if ev.GetEventType().String() != "utimes" { - return zeroValue + return uint64(0) } return ev.Utimes.File.FileFields.MTime } // GetUtimesFileMountId returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileMountId() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "utimes" { - return zeroValue + return uint32(0) } return ev.Utimes.File.FileFields.PathKey.MountID } // GetUtimesFileName returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileName() string { - zeroValue := "" if ev.GetEventType().String() != "utimes" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Utimes.File) } // GetUtimesFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "utimes" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Utimes.File)) } // GetUtimesFilePackageName returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFilePackageName() string { - zeroValue := "" if ev.GetEventType().String() != "utimes" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageName(ev, &ev.Utimes.File) } // GetUtimesFilePackageSourceVersion returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFilePackageSourceVersion() string { - zeroValue := "" if ev.GetEventType().String() != "utimes" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageSourceVersion(ev, &ev.Utimes.File) } // GetUtimesFilePackageVersion returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFilePackageVersion() string { - zeroValue := "" if ev.GetEventType().String() != "utimes" { - return zeroValue + return "" } return ev.FieldHandlers.ResolvePackageVersion(ev, &ev.Utimes.File) } // GetUtimesFilePath returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "utimes" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Utimes.File) } // GetUtimesFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "utimes" { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Utimes.File)) } // GetUtimesFileRights returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileRights() int { - zeroValue := 0 if ev.GetEventType().String() != "utimes" { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveRights(ev, &ev.Utimes.File.FileFields) } // GetUtimesFileUid returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileUid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "utimes" { - return zeroValue + return uint32(0) } return ev.Utimes.File.FileFields.UID } // GetUtimesFileUser returns the value of the field, resolving if necessary func (ev *Event) GetUtimesFileUser() string { - zeroValue := "" if ev.GetEventType().String() != "utimes" { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileFieldsUser(ev, &ev.Utimes.File.FileFields) } // GetUtimesRetval returns the value of the field, resolving if necessary func (ev *Event) GetUtimesRetval() int64 { - zeroValue := int64(0) if ev.GetEventType().String() != "utimes" { - return zeroValue + return int64(0) } return ev.Utimes.SyscallEvent.Retval } diff --git a/pkg/security/secl/model/field_accessors_windows.go b/pkg/security/secl/model/field_accessors_windows.go index ec41b62a4ad1e..81b18240742d2 100644 --- a/pkg/security/secl/model/field_accessors_windows.go +++ b/pkg/security/secl/model/field_accessors_windows.go @@ -16,27 +16,24 @@ import ( // GetContainerCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetContainerCreatedAt() int { - zeroValue := 0 if ev.BaseEvent.ContainerContext == nil { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveContainerCreatedAt(ev, ev.BaseEvent.ContainerContext) } // GetContainerId returns the value of the field, resolving if necessary func (ev *Event) GetContainerId() string { - zeroValue := "" if ev.BaseEvent.ContainerContext == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveContainerID(ev, ev.BaseEvent.ContainerContext) } // GetContainerTags returns the value of the field, resolving if necessary func (ev *Event) GetContainerTags() []string { - zeroValue := []string{} if ev.BaseEvent.ContainerContext == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveContainerTags(ev, ev.BaseEvent.ContainerContext) } @@ -48,366 +45,335 @@ func (ev *Event) GetEventTimestamp() int { // GetExecCmdline returns the value of the field, resolving if necessary func (ev *Event) GetExecCmdline() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.Exec.Process) } // GetExecCmdlineScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetExecCmdlineScrubbed() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessCmdLineScrubbed(ev, ev.Exec.Process) } // GetExecContainerId returns the value of the field, resolving if necessary func (ev *Event) GetExecContainerId() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.Exec.Process.ContainerID } // GetExecCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetExecCreatedAt() int { - zeroValue := 0 if ev.GetEventType().String() != "exec" { - return zeroValue + return 0 } if ev.Exec.Process == nil { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveProcessCreatedAt(ev, ev.Exec.Process) } // GetExecEnvp returns the value of the field, resolving if necessary func (ev *Event) GetExecEnvp() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exec" { - return zeroValue + return []string{} } if ev.Exec.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exec.Process) } // GetExecEnvs returns the value of the field, resolving if necessary func (ev *Event) GetExecEnvs() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exec" { - return zeroValue + return []string{} } if ev.Exec.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exec.Process) } // GetExecExecTime returns the value of the field, resolving if necessary func (ev *Event) GetExecExecTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "exec" { - return zeroValue + return time.Time{} } if ev.Exec.Process == nil { - return zeroValue + return time.Time{} } return ev.Exec.Process.ExecTime } // GetExecExitTime returns the value of the field, resolving if necessary func (ev *Event) GetExecExitTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "exec" { - return zeroValue + return time.Time{} } if ev.Exec.Process == nil { - return zeroValue + return time.Time{} } return ev.Exec.Process.ExitTime } // GetExecFileName returns the value of the field, resolving if necessary func (ev *Event) GetExecFileName() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Exec.Process.FileEvent) } // GetExecFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetExecFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "exec" { - return zeroValue + return 0 } if ev.Exec.Process == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Exec.Process.FileEvent)) } // GetExecFilePath returns the value of the field, resolving if necessary func (ev *Event) GetExecFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "exec" { - return zeroValue + return "" } if ev.Exec.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Exec.Process.FileEvent) } // GetExecFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetExecFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "exec" { - return zeroValue + return 0 } if ev.Exec.Process == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Exec.Process.FileEvent)) } // GetExecPid returns the value of the field, resolving if necessary func (ev *Event) GetExecPid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } return ev.Exec.Process.PIDContext.Pid } // GetExecPpid returns the value of the field, resolving if necessary func (ev *Event) GetExecPpid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exec" { - return zeroValue + return uint32(0) } if ev.Exec.Process == nil { - return zeroValue + return uint32(0) } return ev.Exec.Process.PPid } // GetExitCause returns the value of the field, resolving if necessary func (ev *Event) GetExitCause() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } return ev.Exit.Cause } // GetExitCmdline returns the value of the field, resolving if necessary func (ev *Event) GetExitCmdline() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessCmdLine(ev, ev.Exit.Process) } // GetExitCmdlineScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetExitCmdlineScrubbed() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessCmdLineScrubbed(ev, ev.Exit.Process) } // GetExitCode returns the value of the field, resolving if necessary func (ev *Event) GetExitCode() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } return ev.Exit.Code } // GetExitContainerId returns the value of the field, resolving if necessary func (ev *Event) GetExitContainerId() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.Exit.Process.ContainerID } // GetExitCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetExitCreatedAt() int { - zeroValue := 0 if ev.GetEventType().String() != "exit" { - return zeroValue + return 0 } if ev.Exit.Process == nil { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveProcessCreatedAt(ev, ev.Exit.Process) } // GetExitEnvp returns the value of the field, resolving if necessary func (ev *Event) GetExitEnvp() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exit" { - return zeroValue + return []string{} } if ev.Exit.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvp(ev, ev.Exit.Process) } // GetExitEnvs returns the value of the field, resolving if necessary func (ev *Event) GetExitEnvs() []string { - zeroValue := []string{} if ev.GetEventType().String() != "exit" { - return zeroValue + return []string{} } if ev.Exit.Process == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvs(ev, ev.Exit.Process) } // GetExitExecTime returns the value of the field, resolving if necessary func (ev *Event) GetExitExecTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "exit" { - return zeroValue + return time.Time{} } if ev.Exit.Process == nil { - return zeroValue + return time.Time{} } return ev.Exit.Process.ExecTime } // GetExitExitTime returns the value of the field, resolving if necessary func (ev *Event) GetExitExitTime() time.Time { - zeroValue := time.Time{} if ev.GetEventType().String() != "exit" { - return zeroValue + return time.Time{} } if ev.Exit.Process == nil { - return zeroValue + return time.Time{} } return ev.Exit.Process.ExitTime } // GetExitFileName returns the value of the field, resolving if necessary func (ev *Event) GetExitFileName() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.Exit.Process.FileEvent) } // GetExitFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetExitFileNameLength() int { - zeroValue := 0 if ev.GetEventType().String() != "exit" { - return zeroValue + return 0 } if ev.Exit.Process == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.Exit.Process.FileEvent)) } // GetExitFilePath returns the value of the field, resolving if necessary func (ev *Event) GetExitFilePath() string { - zeroValue := "" if ev.GetEventType().String() != "exit" { - return zeroValue + return "" } if ev.Exit.Process == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.Exit.Process.FileEvent) } // GetExitFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetExitFilePathLength() int { - zeroValue := 0 if ev.GetEventType().String() != "exit" { - return zeroValue + return 0 } if ev.Exit.Process == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.Exit.Process.FileEvent)) } // GetExitPid returns the value of the field, resolving if necessary func (ev *Event) GetExitPid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } return ev.Exit.Process.PIDContext.Pid } // GetExitPpid returns the value of the field, resolving if necessary func (ev *Event) GetExitPpid() uint32 { - zeroValue := uint32(0) if ev.GetEventType().String() != "exit" { - return zeroValue + return uint32(0) } if ev.Exit.Process == nil { - return zeroValue + return uint32(0) } return ev.Exit.Process.PPid } // GetProcessAncestorsCmdline returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsCmdline() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -424,12 +390,11 @@ func (ev *Event) GetProcessAncestorsCmdline() []string { // GetProcessAncestorsCmdlineScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsCmdlineScrubbed() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -446,12 +411,11 @@ func (ev *Event) GetProcessAncestorsCmdlineScrubbed() []string { // GetProcessAncestorsContainerId returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsContainerId() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -468,12 +432,11 @@ func (ev *Event) GetProcessAncestorsContainerId() []string { // GetProcessAncestorsCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsCreatedAt() []int { - zeroValue := []int{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []int{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -490,12 +453,11 @@ func (ev *Event) GetProcessAncestorsCreatedAt() []int { // GetProcessAncestorsEnvp returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsEnvp() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -512,12 +474,11 @@ func (ev *Event) GetProcessAncestorsEnvp() []string { // GetProcessAncestorsEnvs returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsEnvs() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -534,12 +495,11 @@ func (ev *Event) GetProcessAncestorsEnvs() []string { // GetProcessAncestorsFileName returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileName() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -556,12 +516,11 @@ func (ev *Event) GetProcessAncestorsFileName() []string { // GetProcessAncestorsFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFileNameLength() []int { - zeroValue := []int{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []int{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -578,12 +537,11 @@ func (ev *Event) GetProcessAncestorsFileNameLength() []int { // GetProcessAncestorsFilePath returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFilePath() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []string{} } var values []string ctx := eval.NewContext(ev) @@ -600,12 +558,11 @@ func (ev *Event) GetProcessAncestorsFilePath() []string { // GetProcessAncestorsFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsFilePathLength() []int { - zeroValue := []int{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []int{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []int{} } var values []int ctx := eval.NewContext(ev) @@ -622,12 +579,11 @@ func (ev *Event) GetProcessAncestorsFilePathLength() []int { // GetProcessAncestorsPid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsPid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -644,12 +600,11 @@ func (ev *Event) GetProcessAncestorsPid() []uint32 { // GetProcessAncestorsPpid returns the value of the field, resolving if necessary func (ev *Event) GetProcessAncestorsPpid() []uint32 { - zeroValue := []uint32{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []uint32{} } if ev.BaseEvent.ProcessContext.Ancestor == nil { - return zeroValue + return []uint32{} } var values []uint32 ctx := eval.NewContext(ev) @@ -666,120 +621,107 @@ func (ev *Event) GetProcessAncestorsPpid() []uint32 { // GetProcessCmdline returns the value of the field, resolving if necessary func (ev *Event) GetProcessCmdline() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessCmdLine(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessCmdlineScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetProcessCmdlineScrubbed() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveProcessCmdLineScrubbed(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessContainerId returns the value of the field, resolving if necessary func (ev *Event) GetProcessContainerId() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.BaseEvent.ProcessContext.Process.ContainerID } // GetProcessCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetProcessCreatedAt() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } return ev.FieldHandlers.ResolveProcessCreatedAt(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessEnvp returns the value of the field, resolving if necessary func (ev *Event) GetProcessEnvp() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvp(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessEnvs returns the value of the field, resolving if necessary func (ev *Event) GetProcessEnvs() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } return ev.FieldHandlers.ResolveProcessEnvs(ev, &ev.BaseEvent.ProcessContext.Process) } // GetProcessExecTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessExecTime() time.Time { - zeroValue := time.Time{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return time.Time{} } return ev.BaseEvent.ProcessContext.Process.ExecTime } // GetProcessExitTime returns the value of the field, resolving if necessary func (ev *Event) GetProcessExitTime() time.Time { - zeroValue := time.Time{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return time.Time{} } return ev.BaseEvent.ProcessContext.Process.ExitTime } // GetProcessFileName returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileName() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFileBasename(ev, &ev.BaseEvent.ProcessContext.Process.FileEvent) } // GetProcessFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessFileNameLength() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.BaseEvent.ProcessContext.Process.FileEvent)) } // GetProcessFilePath returns the value of the field, resolving if necessary func (ev *Event) GetProcessFilePath() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } return ev.FieldHandlers.ResolveFilePath(ev, &ev.BaseEvent.ProcessContext.Process.FileEvent) } // GetProcessFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessFilePathLength() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.BaseEvent.ProcessContext.Process.FileEvent)) } // GetProcessParentCmdline returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentCmdline() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -789,12 +731,11 @@ func (ev *Event) GetProcessParentCmdline() string { // GetProcessParentCmdlineScrubbed returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentCmdlineScrubbed() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -804,12 +745,11 @@ func (ev *Event) GetProcessParentCmdlineScrubbed() string { // GetProcessParentContainerId returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentContainerId() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -819,12 +759,11 @@ func (ev *Event) GetProcessParentContainerId() string { // GetProcessParentCreatedAt returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentCreatedAt() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return 0 } if !ev.BaseEvent.ProcessContext.HasParent() { return 0 @@ -834,12 +773,11 @@ func (ev *Event) GetProcessParentCreatedAt() int { // GetProcessParentEnvp returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentEnvp() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} @@ -849,12 +787,11 @@ func (ev *Event) GetProcessParentEnvp() []string { // GetProcessParentEnvs returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentEnvs() []string { - zeroValue := []string{} if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return []string{} } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return []string{} } if !ev.BaseEvent.ProcessContext.HasParent() { return []string{} @@ -864,12 +801,11 @@ func (ev *Event) GetProcessParentEnvs() []string { // GetProcessParentFileName returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileName() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -879,24 +815,22 @@ func (ev *Event) GetProcessParentFileName() string { // GetProcessParentFileNameLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFileNameLength() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFileBasename(ev, &ev.BaseEvent.ProcessContext.Parent.FileEvent)) } // GetProcessParentFilePath returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFilePath() string { - zeroValue := "" if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return "" } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return "" } if !ev.BaseEvent.ProcessContext.HasParent() { return "" @@ -906,24 +840,22 @@ func (ev *Event) GetProcessParentFilePath() string { // GetProcessParentFilePathLength returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentFilePathLength() int { - zeroValue := 0 if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return 0 } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return 0 } return len(ev.FieldHandlers.ResolveFilePath(ev, &ev.BaseEvent.ProcessContext.Parent.FileEvent)) } // GetProcessParentPid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentPid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -933,12 +865,11 @@ func (ev *Event) GetProcessParentPid() uint32 { // GetProcessParentPpid returns the value of the field, resolving if necessary func (ev *Event) GetProcessParentPpid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } if ev.BaseEvent.ProcessContext.Parent == nil { - return zeroValue + return uint32(0) } if !ev.BaseEvent.ProcessContext.HasParent() { return uint32(0) @@ -948,18 +879,16 @@ func (ev *Event) GetProcessParentPpid() uint32 { // GetProcessPid returns the value of the field, resolving if necessary func (ev *Event) GetProcessPid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } return ev.BaseEvent.ProcessContext.Process.PIDContext.Pid } // GetProcessPpid returns the value of the field, resolving if necessary func (ev *Event) GetProcessPpid() uint32 { - zeroValue := uint32(0) if ev.BaseEvent.ProcessContext == nil { - return zeroValue + return uint32(0) } return ev.BaseEvent.ProcessContext.Process.PPid } From b710e3deececa4b6c3d0d7b6021f7d214b04c028 Mon Sep 17 00:00:00 2001 From: Jennifer Chen <32009013+jennchenn@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:51:08 -0500 Subject: [PATCH 75/87] [clusteragent/clusterchecks] Add isolate check command to datadog cluster agent (#21049) * Add isolate check command to dca * Clean up isolate command return object * Separate isolate logic into new file * Add changelog * Remove unused log param * fixup! Remove unused log param * Update release note * Add container integrations as codeowner for cli/subcommands/clusterchecks * Simplify for loop logic return early * Remove error return from IsolateCheck call * Rename IsolateResponse.Result to IsIsolated for consistency * Remove reference to checkID in command use text * Add tests for isolation error states * Add more documentation for IsolateCheck logic * fixup! Add more documentation for IsolateCheck logic * fixup! Add container integrations as codeowner for cli/subcommands/clusterchecks * Trigger gitlab sync --- .github/CODEOWNERS | 1 + cmd/cluster-agent/api/v1/clusterchecks.go | 21 ++ pkg/cli/subcommands/clusterchecks/command.go | 61 +++++ .../subcommands/clusterchecks/command_test.go | 18 ++ .../clusterchecks/checks_distribution.go | 14 +- .../clusterchecks/checks_distribution_test.go | 2 +- .../clusterchecks/dispatcher_isolate.go | 62 +++++ .../clusterchecks/dispatcher_isolate_test.go | 225 ++++++++++++++++++ .../clusterchecks/dispatcher_rebalance.go | 1 + pkg/clusteragent/clusterchecks/handler_api.go | 7 + pkg/clusteragent/clusterchecks/types/types.go | 8 + ...and-to-clusterchecks-53ece6c3fbf2720c.yaml | 7 + 12 files changed, 422 insertions(+), 5 deletions(-) create mode 100644 pkg/clusteragent/clusterchecks/dispatcher_isolate.go create mode 100644 pkg/clusteragent/clusterchecks/dispatcher_isolate_test.go create mode 100644 releasenotes-dca/notes/add-isolate-command-to-clusterchecks-53ece6c3fbf2720c.yaml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1156c14c0b4ab..b1a54b4d1567d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -234,6 +234,7 @@ /pkg/aggregator/ @DataDog/agent-metrics-logs /pkg/collector/ @DataDog/agent-metrics-logs /pkg/cli/ @DataDog/agent-shared-components +/pkg/cli/subcommands/clusterchecks @DataDog/container-integrations /pkg/dogstatsd/ @DataDog/agent-metrics-logs /pkg/errors/ @DataDog/agent-shared-components /pkg/forwarder/ @DataDog/agent-metrics-logs @DataDog/agent-shared-components diff --git a/cmd/cluster-agent/api/v1/clusterchecks.go b/cmd/cluster-agent/api/v1/clusterchecks.go index 9dae02174badd..b26a1387cf119 100644 --- a/cmd/cluster-agent/api/v1/clusterchecks.go +++ b/cmd/cluster-agent/api/v1/clusterchecks.go @@ -29,6 +29,7 @@ func installClusterCheckEndpoints(r *mux.Router, sc clusteragent.ServerContext) r.HandleFunc("/clusterchecks/configs/{identifier}", api.WithTelemetryWrapper("getCheckConfigs", getCheckConfigs(sc))).Methods("GET") r.HandleFunc("/clusterchecks/rebalance", api.WithTelemetryWrapper("postRebalanceChecks", postRebalanceChecks(sc))).Methods("POST") r.HandleFunc("/clusterchecks", api.WithTelemetryWrapper("getState", getState(sc))).Methods("GET") + r.HandleFunc("/clusterchecks/isolate/check/{identifier}", api.WithTelemetryWrapper("postIsolateCheck", postIsolateCheck(sc))).Methods("POST") } // RebalancePostPayload struct is for the JSON messages received from a client POST request @@ -121,6 +122,26 @@ func postRebalanceChecks(sc clusteragent.ServerContext) func(w http.ResponseWrit } } +// postIsolateCheck requests that a specified check be isolated in a runner +func postIsolateCheck(sc clusteragent.ServerContext) func(w http.ResponseWriter, r *http.Request) { + if sc.ClusterCheckHandler == nil { + return clusterChecksDisabledHandler + } + + return func(w http.ResponseWriter, r *http.Request) { + if sc.ClusterCheckHandler.RejectOrForwardLeaderQuery(w, r) { + return + } + + vars := mux.Vars(r) + isolateCheckID := vars["identifier"] + + response := sc.ClusterCheckHandler.IsolateCheck(isolateCheckID) + + writeJSONResponse(w, response) + } +} + // getState is used by the clustercheck config func getState(sc clusteragent.ServerContext) func(w http.ResponseWriter, r *http.Request) { if sc.ClusterCheckHandler == nil { diff --git a/pkg/cli/subcommands/clusterchecks/command.go b/pkg/cli/subcommands/clusterchecks/command.go index 21bedb0188c3b..aeebed8057196 100644 --- a/pkg/cli/subcommands/clusterchecks/command.go +++ b/pkg/cli/subcommands/clusterchecks/command.go @@ -40,8 +40,11 @@ type GlobalParams struct { } type cliParams struct { + GlobalParams + checkName string force bool + checkID string } // MakeCommand returns a `clusterchecks` command to be used by cluster-agent @@ -85,6 +88,23 @@ func MakeCommand(globalParamsGetter func() GlobalParams) *cobra.Command { cmd.AddCommand(rebalanceCmd) + isolateCmd := &cobra.Command{ + Use: "isolate", + Short: "Isolates a single check in the cluster runner", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + globalParams := globalParamsGetter() + + return fxutil.OneShot(isolate, + fx.Supply(cliParams), + fx.Supply(bundleParams(globalParams)), + core.Bundle, + ) + }, + } + isolateCmd.Flags().StringVarP(&cliParams.checkID, "checkID", "", "", "the check ID to isolate") + cmd.AddCommand(isolateCmd) + return cmd } @@ -155,3 +175,44 @@ func rebalance(_ log.Component, _ config.Component, cliParams *cliParams) error return nil } + +func isolate(_ log.Component, _ config.Component, cliParams *cliParams) error { + c := util.GetClient(false) // FIX: get certificates right then make this true + if cliParams.checkID == "" { + return fmt.Errorf("checkID must be specified") + } + urlstr := fmt.Sprintf("https://localhost:%v/api/v1/clusterchecks/isolate/check/%s", pkgconfig.Datadog.GetInt("cluster_agent.cmd_port"), cliParams.checkID) + + // Set session token + err := util.SetAuthToken() + if err != nil { + return err + } + + r, err := util.DoPost(c, urlstr, "application/json", bytes.NewBuffer([]byte{})) + if err != nil { + var errMap = make(map[string]string) + json.Unmarshal(r, &errMap) //nolint:errcheck + // If the error has been marshalled into a json object, check it and return it properly + if e, found := errMap["error"]; found { + err = fmt.Errorf(e) + } + + fmt.Printf(` + Could not reach agent: %v + Make sure the agent is running before requesting to isolate a cluster check. + Contact support if you continue having issues.`, err) + + return err + } + + var response types.IsolateResponse + + json.Unmarshal(r, &response) //nolint:errcheck + if response.IsIsolated { + fmt.Printf("Check %s isolated successfully on node %s\n", response.CheckID, response.CheckNode) + } else { + fmt.Printf("Check %s could not be isolated: %s\n", response.CheckID, response.Reason) + } + return nil +} diff --git a/pkg/cli/subcommands/clusterchecks/command_test.go b/pkg/cli/subcommands/clusterchecks/command_test.go index 61736ac285ade..388005f7bcd7f 100644 --- a/pkg/cli/subcommands/clusterchecks/command_test.go +++ b/pkg/cli/subcommands/clusterchecks/command_test.go @@ -10,8 +10,10 @@ package clusterchecks import ( "testing" + "github.com/DataDog/datadog-agent/comp/core" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/spf13/cobra" + "github.com/stretchr/testify/require" ) func TestCommand(t *testing.T) { @@ -41,3 +43,19 @@ func TestRebalance(t *testing.T) { rebalance, func() {}) } + +func TestIsolate(t *testing.T) { + commands := []*cobra.Command{ + MakeCommand(func() GlobalParams { + return GlobalParams{} + }), + } + + fxutil.TestOneShotSubcommand(t, + commands, + []string{"clusterchecks", "isolate", "--checkID", "checkID"}, + isolate, + func(cliParams *cliParams, coreParams core.BundleParams) { + require.Equal(t, "checkID", cliParams.checkID) + }) +} diff --git a/pkg/clusteragent/clusterchecks/checks_distribution.go b/pkg/clusteragent/clusterchecks/checks_distribution.go index cba283511a89c..989b4e7e67a97 100644 --- a/pkg/clusteragent/clusterchecks/checks_distribution.go +++ b/pkg/clusteragent/clusterchecks/checks_distribution.go @@ -59,13 +59,19 @@ func newChecksDistribution(workersPerRunner map[string]int) checksDistribution { // leastBusyRunner returns the runner with the lowest utilization. If there are // several options, it gives preference to preferredRunner. If preferredRunner // is not among the runners with the lowest utilization, it gives precedence to -// the runner with the lowest number of checks deployed. -func (distribution *checksDistribution) leastBusyRunner(preferredRunner string) string { +// the runner with the lowest number of checks deployed. excludeRunner can be set +// to avoid assigning a check to a specific runner. +func (distribution *checksDistribution) leastBusyRunner(preferredRunner string, excludeRunner string) string { leastBusyRunner := "" minUtilization := 0.0 numChecksLeastBusyRunner := 0 for runnerName, runnerStatus := range distribution.Runners { + // Allow exclusion of a runner from selection + if runnerName == excludeRunner { + continue + } + runnerUtilization := runnerStatus.utilization() runnerNumChecks := runnerStatus.NumChecks @@ -84,8 +90,8 @@ func (distribution *checksDistribution) leastBusyRunner(preferredRunner string) return leastBusyRunner } -func (distribution *checksDistribution) addToLeastBusy(checkID string, workersNeeded float64, preferredRunner string) { - leastBusy := distribution.leastBusyRunner(preferredRunner) +func (distribution *checksDistribution) addToLeastBusy(checkID string, workersNeeded float64, preferredRunner string, excludeRunner string) { + leastBusy := distribution.leastBusyRunner(preferredRunner, excludeRunner) if leastBusy == "" { return } diff --git a/pkg/clusteragent/clusterchecks/checks_distribution_test.go b/pkg/clusteragent/clusterchecks/checks_distribution_test.go index 9c2d0f58c3e24..0972ff99fbe05 100644 --- a/pkg/clusteragent/clusterchecks/checks_distribution_test.go +++ b/pkg/clusteragent/clusterchecks/checks_distribution_test.go @@ -137,7 +137,7 @@ func TestAddToLeastBusy(t *testing.T) { distribution.addCheck(checkID, checkStatus.WorkersNeeded, checkStatus.Runner) } - distribution.addToLeastBusy("newCheck", 10, test.preferredRunner) + distribution.addToLeastBusy("newCheck", 10, test.preferredRunner, "") assert.Equal(t, test.expectedPlacement, distribution.runnerForCheck("newCheck")) }) diff --git a/pkg/clusteragent/clusterchecks/dispatcher_isolate.go b/pkg/clusteragent/clusterchecks/dispatcher_isolate.go new file mode 100644 index 0000000000000..ad54bc5a27489 --- /dev/null +++ b/pkg/clusteragent/clusterchecks/dispatcher_isolate.go @@ -0,0 +1,62 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build clusterchecks + +package clusterchecks + +import "github.com/DataDog/datadog-agent/pkg/clusteragent/clusterchecks/types" + +func (d *dispatcher) isolateCheck(isolateCheckID string) types.IsolateResponse { + // Update stats prior to starting isolate to ensure all checks are accounted for + d.updateRunnersStats() + currentDistribution := d.currentDistribution() + + // If there is only one runner, we cannot isolate the check + if len(currentDistribution.runnerWorkers()) == 1 { + return types.IsolateResponse{ + CheckID: isolateCheckID, + CheckNode: "", + IsIsolated: false, + Reason: "No other runners available", + } + } + + isolateNode := currentDistribution.runnerForCheck(isolateCheckID) + if isolateNode == "" { + return types.IsolateResponse{ + CheckID: isolateCheckID, + CheckNode: "", + IsIsolated: false, + Reason: "Unable to find check", + } + } + + proposedDistribution := newChecksDistribution(currentDistribution.runnerWorkers()) + + for _, checkID := range currentDistribution.checksSortedByWorkersNeeded() { + if checkID == isolateCheckID { + // Keep the check to be isolated on its current runner + continue + } + + workersNeededForCheck := currentDistribution.workersNeededForCheck(checkID) + runnerForCheck := currentDistribution.runnerForCheck(checkID) + + proposedDistribution.addToLeastBusy( + checkID, + workersNeededForCheck, + runnerForCheck, + isolateNode, + ) + } + + d.applyDistribution(proposedDistribution, currentDistribution) + return types.IsolateResponse{ + CheckID: isolateCheckID, + CheckNode: isolateNode, + IsIsolated: true, + } +} diff --git a/pkg/clusteragent/clusterchecks/dispatcher_isolate_test.go b/pkg/clusteragent/clusterchecks/dispatcher_isolate_test.go new file mode 100644 index 0000000000000..b12e70c7edb46 --- /dev/null +++ b/pkg/clusteragent/clusterchecks/dispatcher_isolate_test.go @@ -0,0 +1,225 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build clusterchecks + +package clusterchecks + +import ( + "testing" + + "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" + "github.com/DataDog/datadog-agent/pkg/clusteragent/clusterchecks/types" + checkid "github.com/DataDog/datadog-agent/pkg/collector/check/id" + "github.com/DataDog/datadog-agent/pkg/config" + "github.com/stretchr/testify/assert" +) + +func TestIsolateCheckSuccessful(t *testing.T) { + testDispatcher := newDispatcher() + testDispatcher.store.nodes["A"] = newNodeStore("A", "") + testDispatcher.store.nodes["A"].workers = config.DefaultNumWorkers + testDispatcher.store.nodes["B"] = newNodeStore("B", "") + testDispatcher.store.nodes["B"].workers = config.DefaultNumWorkers + + testDispatcher.store.nodes["A"].clcRunnerStats = map[string]types.CLCRunnerStats{ + "checkA0": { + AverageExecutionTime: 50, + MetricSamples: 10, + IsClusterCheck: true, + }, + "checkA1": { + AverageExecutionTime: 20, + MetricSamples: 10, + IsClusterCheck: true, + }, + "checkA2": { + AverageExecutionTime: 100, + MetricSamples: 10, + IsClusterCheck: true, + }, + } + + testDispatcher.store.nodes["B"].clcRunnerStats = map[string]types.CLCRunnerStats{ + "checkB0": { + AverageExecutionTime: 50, + MetricSamples: 10, + IsClusterCheck: true, + }, + "checkB1": { + AverageExecutionTime: 20, + MetricSamples: 10, + IsClusterCheck: true, + }, + "checkB2": { + AverageExecutionTime: 100, + MetricSamples: 10, + IsClusterCheck: true, + }, + } + testDispatcher.store.idToDigest = map[checkid.ID]string{ + "checkA0": "digestA0", + "checkA1": "digestA1", + "checkA2": "digestA2", + "checkB0": "digestB0", + "checkB1": "digestB1", + "checkB2": "digestB2", + } + testDispatcher.store.digestToConfig = map[string]integration.Config{ + "digestA0": {}, + "digestA1": {}, + "digestA2": {}, + "digestB0": {}, + "digestB1": {}, + "digestB2": {}, + } + testDispatcher.store.digestToNode = map[string]string{ + "digestA0": "A", + "digestA1": "A", + "digestA2": "A", + "digestB0": "B", + "digestB1": "B", + "digestB2": "B", + } + + response := testDispatcher.isolateCheck("checkA2") + assert.EqualValues(t, response.CheckID, "checkA2") + assert.EqualValues(t, response.CheckNode, "A") + assert.True(t, response.IsIsolated) + assert.EqualValues(t, response.Reason, "") + assert.EqualValues(t, len(testDispatcher.store.nodes["A"].clcRunnerStats), 1) + assert.EqualValues(t, len(testDispatcher.store.nodes["B"].clcRunnerStats), 5) + _, containsIsolatedCheck := testDispatcher.store.nodes["A"].clcRunnerStats["checkA2"] + assert.True(t, containsIsolatedCheck) + + requireNotLocked(t, testDispatcher.store) +} + +func TestIsolateNonExistentCheckFails(t *testing.T) { + testDispatcher := newDispatcher() + testDispatcher.store.nodes["A"] = newNodeStore("A", "") + testDispatcher.store.nodes["A"].workers = config.DefaultNumWorkers + testDispatcher.store.nodes["B"] = newNodeStore("B", "") + testDispatcher.store.nodes["B"].workers = config.DefaultNumWorkers + + testDispatcher.store.nodes["A"].clcRunnerStats = map[string]types.CLCRunnerStats{ + "checkA0": { + AverageExecutionTime: 50, + MetricSamples: 10, + IsClusterCheck: true, + }, + "checkA1": { + AverageExecutionTime: 20, + MetricSamples: 10, + IsClusterCheck: true, + }, + "checkA2": { + AverageExecutionTime: 100, + MetricSamples: 10, + IsClusterCheck: true, + }, + } + + testDispatcher.store.nodes["B"].clcRunnerStats = map[string]types.CLCRunnerStats{ + "checkB0": { + AverageExecutionTime: 50, + MetricSamples: 10, + IsClusterCheck: true, + }, + "checkB1": { + AverageExecutionTime: 20, + MetricSamples: 10, + IsClusterCheck: true, + }, + "checkB2": { + AverageExecutionTime: 100, + MetricSamples: 10, + IsClusterCheck: true, + }, + } + testDispatcher.store.idToDigest = map[checkid.ID]string{ + "checkA0": "digestA0", + "checkA1": "digestA1", + "checkA2": "digestA2", + "checkB0": "digestB0", + "checkB1": "digestB1", + "checkB2": "digestB2", + } + testDispatcher.store.digestToConfig = map[string]integration.Config{ + "digestA0": {}, + "digestA1": {}, + "digestA2": {}, + "digestB0": {}, + "digestB1": {}, + "digestB2": {}, + } + testDispatcher.store.digestToNode = map[string]string{ + "digestA0": "A", + "digestA1": "A", + "digestA2": "A", + "digestB0": "B", + "digestB1": "B", + "digestB2": "B", + } + + response := testDispatcher.isolateCheck("checkA5") + assert.EqualValues(t, response.CheckID, "checkA5") + assert.EqualValues(t, response.CheckNode, "") + assert.False(t, response.IsIsolated) + assert.EqualValues(t, response.Reason, "Unable to find check") + assert.EqualValues(t, len(testDispatcher.store.nodes["A"].clcRunnerStats), 3) + assert.EqualValues(t, len(testDispatcher.store.nodes["B"].clcRunnerStats), 3) + + requireNotLocked(t, testDispatcher.store) +} + +func TestIsolateCheckOnlyOneRunnerFails(t *testing.T) { + testDispatcher := newDispatcher() + testDispatcher.store.nodes["A"] = newNodeStore("A", "") + testDispatcher.store.nodes["A"].workers = config.DefaultNumWorkers + + testDispatcher.store.nodes["A"].clcRunnerStats = map[string]types.CLCRunnerStats{ + "checkA0": { + AverageExecutionTime: 50, + MetricSamples: 10, + IsClusterCheck: true, + }, + "checkA1": { + AverageExecutionTime: 20, + MetricSamples: 10, + IsClusterCheck: true, + }, + "checkA2": { + AverageExecutionTime: 100, + MetricSamples: 10, + IsClusterCheck: true, + }, + } + + testDispatcher.store.idToDigest = map[checkid.ID]string{ + "checkA0": "digestA0", + "checkA1": "digestA1", + "checkA2": "digestA2", + } + testDispatcher.store.digestToConfig = map[string]integration.Config{ + "digestA0": {}, + "digestA1": {}, + "digestA2": {}, + } + testDispatcher.store.digestToNode = map[string]string{ + "digestA0": "A", + "digestA1": "A", + "digestA2": "A", + } + + response := testDispatcher.isolateCheck("checkA1") + assert.EqualValues(t, response.CheckID, "checkA1") + assert.EqualValues(t, response.CheckNode, "") + assert.False(t, response.IsIsolated) + assert.EqualValues(t, response.Reason, "No other runners available") + assert.EqualValues(t, len(testDispatcher.store.nodes["A"].clcRunnerStats), 3) + + requireNotLocked(t, testDispatcher.store) +} diff --git a/pkg/clusteragent/clusterchecks/dispatcher_rebalance.go b/pkg/clusteragent/clusterchecks/dispatcher_rebalance.go index c044efdfa31d4..d1e28bce5fe83 100644 --- a/pkg/clusteragent/clusterchecks/dispatcher_rebalance.go +++ b/pkg/clusteragent/clusterchecks/dispatcher_rebalance.go @@ -315,6 +315,7 @@ func (d *dispatcher) rebalanceUsingUtilization(force bool) []types.RebalanceResp checkID, currentChecksDistribution.workersNeededForCheck(checkID), currentChecksDistribution.runnerForCheck(checkID), + "", ) } diff --git a/pkg/clusteragent/clusterchecks/handler_api.go b/pkg/clusteragent/clusterchecks/handler_api.go index c13ddd0d69807..71aee7c5838c8 100644 --- a/pkg/clusteragent/clusterchecks/handler_api.go +++ b/pkg/clusteragent/clusterchecks/handler_api.go @@ -119,3 +119,10 @@ func (h *Handler) RebalanceClusterChecks(force bool) ([]types.RebalanceResponse, return response, nil } + +// IsolateCheck triggers an attempt to isolate a check in a runner. Other checks +// will be redistributed to other runners using the existing rebalancing logic. +func (h *Handler) IsolateCheck(isolateCheckID string) types.IsolateResponse { + response := h.dispatcher.isolateCheck(isolateCheckID) + return response +} diff --git a/pkg/clusteragent/clusterchecks/types/types.go b/pkg/clusteragent/clusterchecks/types/types.go index 818597bb1541c..2ce00e8937118 100644 --- a/pkg/clusteragent/clusterchecks/types/types.go +++ b/pkg/clusteragent/clusterchecks/types/types.go @@ -39,6 +39,14 @@ type RebalanceResponse struct { DestDiff int `json:"dest_diff"` } +// IsolateResponse holds the DCA response for an isolate request +type IsolateResponse struct { + CheckID string `json:"check_id"` + CheckNode string `json:"check_node"` + IsIsolated bool `json:"is_isolated"` + Reason string `json:"reason"` +} + // ConfigResponse holds the DCA response for a config query type ConfigResponse struct { LastChange int64 `json:"last_change"` diff --git a/releasenotes-dca/notes/add-isolate-command-to-clusterchecks-53ece6c3fbf2720c.yaml b/releasenotes-dca/notes/add-isolate-command-to-clusterchecks-53ece6c3fbf2720c.yaml new file mode 100644 index 0000000000000..cc22686bdd20d --- /dev/null +++ b/releasenotes-dca/notes/add-isolate-command-to-clusterchecks-53ece6c3fbf2720c.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Add isolate command to clusterchecks to make it easier to pinpoint + a check that that is causing high CPU/memory usage. Command can be + run in the cluster agent with: + `datadog-cluster-agent clusterchecks isolate --checkID=` From 52816a0911918bb3f52cf3b07ebc6b702275e3c2 Mon Sep 17 00:00:00 2001 From: Maxime mouial Date: Fri, 24 Nov 2023 15:04:30 +0100 Subject: [PATCH 76/87] Adding a callback based method to the secrets component The secrets component can now notify the caller when resolving a secret. This allows the config package to only overwrite the setting using secrets instead of the entire configuration. --- comp/core/secrets/component.go | 7 +- comp/core/secrets/secretsimpl/fetch_secret.go | 6 +- .../secrets/secretsimpl/fetch_secret_test.go | 8 +- comp/core/secrets/secretsimpl/info.tmpl | 4 +- .../core/secrets/secretsimpl/info_nix_test.go | 16 +- comp/core/secrets/secretsimpl/secrets.go | 140 +++++--------- comp/core/secrets/secretsimpl/secrets_mock.go | 28 ++- comp/core/secrets/secretsimpl/secrets_test.go | 171 ++++++++++-------- comp/core/secrets/secretsimpl/walker.go | 114 ++++++++++++ comp/core/secrets/secretsimpl/walker_test.go | 134 ++++++++++++++ comp/core/secrets/type.go | 3 + pkg/autodiscovery/secrets.go | 8 +- pkg/autodiscovery/secrets_test.go | 26 +-- pkg/config/config.go | 14 +- pkg/config/config_secret_test.go | 21 +-- 15 files changed, 468 insertions(+), 232 deletions(-) create mode 100644 comp/core/secrets/secretsimpl/walker.go create mode 100644 comp/core/secrets/secretsimpl/walker_test.go diff --git a/comp/core/secrets/component.go b/comp/core/secrets/component.go index f0c1dc81e3985..8e021e3659bb0 100644 --- a/comp/core/secrets/component.go +++ b/comp/core/secrets/component.go @@ -18,6 +18,9 @@ type Component interface { Configure(command string, arguments []string, timeout, maxSize int, groupExecPerm, removeLinebreak bool) // Get debug information and write it to the parameter GetDebugInfo(w io.Writer) - // Decrypt the given handle and return the corresponding secret value - Decrypt(data []byte, origin string) ([]byte, error) + // Resolve resolves the secrets in the given yaml data by replacing secrets handles by their corresponding secret value + Resolve(data []byte, origin string) ([]byte, error) + // ResolveWithCallback resolves the secrets in the given yaml data calling the callback with the YAML path of + // the secret handle and its value + ResolveWithCallback(data []byte, origin string, callback ResolveCallback) error } diff --git a/comp/core/secrets/secretsimpl/fetch_secret.go b/comp/core/secrets/secretsimpl/fetch_secret.go index c3841608be909..e140ad29534d9 100644 --- a/comp/core/secrets/secretsimpl/fetch_secret.go +++ b/comp/core/secrets/secretsimpl/fetch_secret.go @@ -133,11 +133,11 @@ func (r *secretResolver) fetchSecret(secretsHandle []string) (map[string]string, for _, sec := range secretsHandle { v, ok := secrets[sec] if !ok { - return nil, fmt.Errorf("secret handle '%s' was not decrypted by the secret_backend_command", sec) + return nil, fmt.Errorf("secret handle '%s' was not resolved by the secret_backend_command", sec) } if v.ErrorMsg != "" { - return nil, fmt.Errorf("an error occurred while decrypting '%s': %s", sec, v.ErrorMsg) + return nil, fmt.Errorf("an error occurred while resolving '%s': %s", sec, v.ErrorMsg) } if r.removeTrailingLinebreak { @@ -145,7 +145,7 @@ func (r *secretResolver) fetchSecret(secretsHandle []string) (map[string]string, } if v.Value == "" { - return nil, fmt.Errorf("decrypted secret for '%s' is empty", sec) + return nil, fmt.Errorf("resolved secret for '%s' is empty", sec) } // add it to the cache diff --git a/comp/core/secrets/secretsimpl/fetch_secret_test.go b/comp/core/secrets/secretsimpl/fetch_secret_test.go index bbd23a699b495..0212fd64d5af0 100644 --- a/comp/core/secrets/secretsimpl/fetch_secret_test.go +++ b/comp/core/secrets/secretsimpl/fetch_secret_test.go @@ -201,7 +201,7 @@ func TestFetchSecretMissingSecret(t *testing.T) { resolver.commandHookFunc = func(string) ([]byte, error) { return []byte("{}"), nil } _, err := resolver.fetchSecret(secrets) assert.NotNil(t, err) - assert.Equal(t, "secret handle 'handle1' was not decrypted by the secret_backend_command", err.Error()) + assert.Equal(t, "secret handle 'handle1' was not resolved by the secret_backend_command", err.Error()) } func TestFetchSecretErrorForHandle(t *testing.T) { @@ -211,7 +211,7 @@ func TestFetchSecretErrorForHandle(t *testing.T) { } _, err := resolver.fetchSecret([]string{"handle1"}) assert.NotNil(t, err) - assert.Equal(t, "an error occurred while decrypting 'handle1': some error", err.Error()) + assert.Equal(t, "an error occurred while resolving 'handle1': some error", err.Error()) } func TestFetchSecretEmptyValue(t *testing.T) { @@ -221,14 +221,14 @@ func TestFetchSecretEmptyValue(t *testing.T) { } _, err := resolver.fetchSecret([]string{"handle1"}) assert.NotNil(t, err) - assert.Equal(t, "decrypted secret for 'handle1' is empty", err.Error()) + assert.Equal(t, "resolved secret for 'handle1' is empty", err.Error()) resolver.commandHookFunc = func(string) ([]byte, error) { return []byte("{\"handle1\":{\"value\": \"\"}}"), nil } _, err = resolver.fetchSecret([]string{"handle1"}) assert.NotNil(t, err) - assert.Equal(t, "decrypted secret for 'handle1' is empty", err.Error()) + assert.Equal(t, "resolved secret for 'handle1' is empty", err.Error()) } func TestFetchSecret(t *testing.T) { diff --git a/comp/core/secrets/secretsimpl/info.tmpl b/comp/core/secrets/secretsimpl/info.tmpl index 2c56659e88806..53f30f3ce3851 100644 --- a/comp/core/secrets/secretsimpl/info.tmpl +++ b/comp/core/secrets/secretsimpl/info.tmpl @@ -10,8 +10,8 @@ Permissions Detail: {{- end }} === Secrets stats === -Number of secrets decrypted: {{ len .Handles }} -Secrets handle decrypted: +Number of secrets resolved: {{ len .Handles }} +Secrets handle resolved: {{ range $handle, $places := .Handles }} - '{{ $handle }}': {{- range $place := $places }} diff --git a/comp/core/secrets/secretsimpl/info_nix_test.go b/comp/core/secrets/secretsimpl/info_nix_test.go index 7deb57ef2be47..1b170077d15a7 100644 --- a/comp/core/secrets/secretsimpl/info_nix_test.go +++ b/comp/core/secrets/secretsimpl/info_nix_test.go @@ -77,9 +77,9 @@ func TestDebugInfo(t *testing.T) { return res, nil } - _, err := resolver.Decrypt(testConf, "test") + _, err := resolver.Resolve(testConf, "test") require.NoError(t, err) - _, err = resolver.Decrypt(testConfInfo, "test2") + _, err = resolver.Resolve(testConfInfo, "test2") require.NoError(t, err) var buffer bytes.Buffer @@ -95,8 +95,8 @@ Owner: ` + currentUser + ` Group: ` + currentGroup + ` === Secrets stats === -Number of secrets decrypted: 3 -Secrets handle decrypted: +Number of secrets resolved: 3 +Secrets handle resolved: - 'pass1': used in 'test' configuration in entry 'instances/password' @@ -121,9 +121,9 @@ func TestDebugInfoError(t *testing.T) { return res, nil } - _, err := resolver.Decrypt(testConf, "test") + _, err := resolver.Resolve(testConf, "test") require.NoError(t, err) - _, err = resolver.Decrypt(testConfInfo, "test2") + _, err = resolver.Resolve(testConfInfo, "test2") require.NoError(t, err) var buffer bytes.Buffer @@ -137,8 +137,8 @@ Permissions Detail: Could not stat some_command: no such file or directory === Secrets stats === -Number of secrets decrypted: 3 -Secrets handle decrypted: +Number of secrets resolved: 3 +Secrets handle resolved: - 'pass1': used in 'test' configuration in entry 'instances/password' diff --git a/comp/core/secrets/secretsimpl/secrets.go b/comp/core/secrets/secretsimpl/secrets.go index 787e050b7ec14..2f436c380186f 100644 --- a/comp/core/secrets/secretsimpl/secrets.go +++ b/comp/core/secrets/secretsimpl/secrets.go @@ -186,75 +186,6 @@ func (r *secretResolver) Configure(command string, arguments []string, timeout, } } -type walkerCallback func([]string, string) (string, error) - -func walkSlice(data []interface{}, yamlPath []string, callback walkerCallback) error { - for idx, k := range data { - switch v := k.(type) { - case string: - newValue, err := callback(yamlPath, v) - if err != nil { - return err - } - data[idx] = newValue - case map[interface{}]interface{}: - if err := walkHash(v, yamlPath, callback); err != nil { - return err - } - case []interface{}: - if err := walkSlice(v, yamlPath, callback); err != nil { - return err - } - } - } - return nil -} - -func walkHash(data map[interface{}]interface{}, yamlPath []string, callback walkerCallback) error { - for k := range data { - path := yamlPath - if newkey, ok := k.(string); ok { - path = append(path, newkey) - } - - switch v := data[k].(type) { - case string: - newValue, err := callback(path, v) - if err != nil { - return err - } - data[k] = newValue - case map[interface{}]interface{}: - if err := walkHash(v, path, callback); err != nil { - return err - } - case []interface{}: - if err := walkSlice(v, path, callback); err != nil { - return err - } - } - } - return nil -} - -// walk will go through loaded yaml and call callback on every strings allowing -// the callback to overwrite the string value -func walk(data *interface{}, yamlPath []string, callback walkerCallback) error { - switch v := (*data).(type) { - case string: - newValue, err := callback(yamlPath, v) - if err != nil { - return err - } - *data = newValue - case map[interface{}]interface{}: - return walkHash(v, yamlPath, callback) - case []interface{}: - return walkSlice(v, yamlPath, callback) - } - return nil -} - func isEnc(str string) (bool, string) { // trimming space and tabs str = strings.Trim(str, " ") @@ -264,9 +195,20 @@ func isEnc(str string) (bool, string) { return false, "" } -// Decrypt replaces all encrypted secrets in data by executing -// "secret_backend_command" once if all secrets aren't present in the cache. -func (r *secretResolver) Decrypt(data []byte, origin string) ([]byte, error) { +// Resolve replaces all encrypted secrets in data by executing "secret_backend_command" once if all secrets aren't +// present in the cache. +func (r *secretResolver) Resolve(data []byte, origin string) ([]byte, error) { + return r.resolve(data, origin, nil) +} + +// ResolveWithCallback resolves the secrets in the given yaml data calling the callback with the YAML path of +// the secret handle and its value +func (r *secretResolver) ResolveWithCallback(data []byte, origin string, cb secrets.ResolveCallback) error { + _, err := r.resolve(data, origin, cb) + return err +} + +func (r *secretResolver) resolve(data []byte, origin string, notifyCb secrets.ResolveCallback) ([]byte, error) { if !r.enabled { log.Infof("Agent secrets is disabled by caller") return nil, nil @@ -284,11 +226,10 @@ func (r *secretResolver) Decrypt(data []byte, origin string) ([]byte, error) { // First we collect all new handles in the config newHandles := []string{} haveSecret := false - err = walk( - &config, - nil, - func(yamlPath []string, str string) (string, error) { - if ok, handle := isEnc(str); ok { + + w := &walker{ + resolver: func(yamlPath []string, value string) (string, error) { + if ok, handle := isEnc(value); ok { haveSecret = true // Check if we already know this secret if secret, ok := r.cache[handle]; ok { @@ -298,10 +239,14 @@ func (r *secretResolver) Decrypt(data []byte, origin string) ([]byte, error) { return secret, nil } newHandles = append(newHandles, handle) + return value, nil } - return str, nil - }) - if err != nil { + return value, nil + }, + notifier: notifyCb, + } + + if err := w.walk(&config); err != nil { return nil, err } @@ -324,25 +269,24 @@ func (r *secretResolver) Decrypt(data []byte, origin string) ([]byte, error) { return nil, err } - // Replace all new encrypted secrets in the config - err = walk( - &config, - nil, - func(yamlPath []string, str string) (string, error) { - if ok, handle := isEnc(str); ok { - if secret, ok := secrets[handle]; ok { - log.Debugf("Secret '%s' was retrieved from executable", handle) - // keep track of place where a handle was found - r.registerSecretOrigin(handle, origin, yamlPath) - return secret, nil - } - // This should never happen since fetchSecret will return an error - // if not every handles have been fetched. - return str, fmt.Errorf("unknown secret '%s'", handle) + w.resolver = func(yamlPath []string, value string) (string, error) { + if ok, handle := isEnc(value); ok { + if secret, ok := secrets[handle]; ok { + log.Debugf("Secret '%s' was successfully resolved", handle) + // keep track of place where a handle was found + r.registerSecretOrigin(handle, origin, yamlPath) + return secret, nil } - return str, nil - }) - if err != nil { + + // This should never happen since fetchSecret will return an error if not every handle have + // been fetched. + return "", fmt.Errorf("unknown secret '%s'", handle) + } + return value, nil + } + + // Replace all newly resolved secrets in the config + if err := w.walk(&config); err != nil { return nil, err } } diff --git a/comp/core/secrets/secretsimpl/secrets_mock.go b/comp/core/secrets/secretsimpl/secrets_mock.go index 45bf33d9d276a..cfa456e6bef4e 100644 --- a/comp/core/secrets/secretsimpl/secrets_mock.go +++ b/comp/core/secrets/secretsimpl/secrets_mock.go @@ -16,9 +16,15 @@ import ( "go.uber.org/fx" ) +type callbackArgs struct { + yamlPath []string + value any +} + // MockSecretResolver is a mock of the secret Component useful for testing type MockSecretResolver struct { - resolve map[string]string + resolve map[string]string + callbacks []callbackArgs } var _ secrets.Component = (*MockSecretResolver)(nil) @@ -29,13 +35,19 @@ func (m *MockSecretResolver) Configure(_ string, _ []string, _, _ int, _, _ bool // GetDebugInfo is not implemented func (m *MockSecretResolver) GetDebugInfo(_ io.Writer) {} -// Inject adds data to be decrypted, by returning the value for the given key +// Inject adds data to be resolved, by returning the value for the given key func (m *MockSecretResolver) Inject(key, value string) { m.resolve[key] = value } -// Decrypt returns the secret value based upon the injected data -func (m *MockSecretResolver) Decrypt(data []byte, _ string) ([]byte, error) { +// InjectCallback adds to the list of callbacks that will be used to mock ResolveWithCallback. Each injected callback +// will equal a call to the callback givent to 'ResolveWithCallback' +func (m *MockSecretResolver) InjectCallback(yamlPath []string, value any) { + m.callbacks = append(m.callbacks, callbackArgs{yamlPath: yamlPath, value: value}) +} + +// Resolve returns the secret value based upon the injected data +func (m *MockSecretResolver) Resolve(data []byte, _ string) ([]byte, error) { re := regexp.MustCompile(`ENC\[(.*?)\]`) result := re.ReplaceAllStringFunc(string(data), func(in string) string { key := in[4 : len(in)-1] @@ -44,6 +56,14 @@ func (m *MockSecretResolver) Decrypt(data []byte, _ string) ([]byte, error) { return []byte(result), nil } +// ResolveWithCallback mocks the ResolveWithCallback method of the secrets Component +func (m *MockSecretResolver) ResolveWithCallback(_ []byte, _ string, cb secrets.ResolveCallback) error { + for _, call := range m.callbacks { + cb(call.yamlPath, call.value) + } + return nil +} + // NewMockSecretResolver constructs a MockSecretResolver func NewMockSecretResolver() *MockSecretResolver { return &MockSecretResolver{resolve: make(map[string]string)} diff --git a/comp/core/secrets/secretsimpl/secrets_test.go b/comp/core/secrets/secretsimpl/secrets_test.go index 81e37cd7cd829..56ef9557c73de 100644 --- a/comp/core/secrets/secretsimpl/secrets_test.go +++ b/comp/core/secrets/secretsimpl/secrets_test.go @@ -8,11 +8,11 @@ package secretsimpl import ( "fmt" "sort" + "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - yaml "gopkg.in/yaml.v2" ) var ( @@ -52,7 +52,7 @@ instances: user: test2 `) - testConfDecrypted = `instances: + testConfResolveed = `instances: - password: password1 user: test - password: password2 @@ -80,7 +80,7 @@ keys_with_dash_string_value: foo: "-" `) - testConfDecryptedDash = `keys_with_dash_string_value: + testConfResolveedDash = `keys_with_dash_string_value: foo: '-' some_encoded_password: password1 ` @@ -97,7 +97,7 @@ some_encoded_password: password1 some_encoded_password: ENC[pass1] `) - testConfDecryptedMultiline = `some_encoded_password: | + testConfResolveedMultiline = `some_encoded_password: | password1 ` testConfMultilineOrigin = handleToContext{ @@ -115,7 +115,7 @@ some: data: ENC[pass1] `) - testConfDecryptedNested = `some: + testConfResolveedNested = `some: encoded: data: password1 ` @@ -127,6 +127,35 @@ some: }, }, } + + testConfNestedMultiple = []byte(`--- +top_level: ENC[pass1] +some: + second_level: ENC[pass2] + encoded: + third_level: ENC[pass3] +`) + + testConfNestedOriginMultiple = handleToContext{ + "pass1": []secretContext{ + { + origin: "test", + yamlPath: "top_level", + }, + }, + "pass2": []secretContext{ + { + origin: "test", + yamlPath: "some/second_level", + }, + }, + "pass3": []secretContext{ + { + origin: "test", + yamlPath: "some/encoded/third_level", + }, + }, + } ) func TestIsEnc(t *testing.T) { @@ -157,77 +186,19 @@ func TestIsEnc(t *testing.T) { assert.Equal(t, "test", secret) } -func TestWalkerError(t *testing.T) { - var config interface{} - err := yaml.Unmarshal(testYamlHash, &config) - require.NoError(t, err) - - err = walk(&config, nil, func([]string, string) (string, error) { - return "", fmt.Errorf("some error") - }) - assert.NotNil(t, err) -} - -func TestWalkerSimple(t *testing.T) { - var config interface{} - err := yaml.Unmarshal([]byte("test"), &config) - require.NoError(t, err) - - stringsCollected := []string{} - err = walk(&config, nil, func(_ []string, str string) (string, error) { - stringsCollected = append(stringsCollected, str) - return str + "_verified", nil - }) - require.NoError(t, err) - - assert.Equal(t, []string{"test"}, stringsCollected) - - updatedConf, err := yaml.Marshal(config) - require.NoError(t, err) - assert.Equal(t, string("test_verified\n"), string(updatedConf)) -} - -func TestWalkerComplex(t *testing.T) { - var config interface{} - err := yaml.Unmarshal(testYamlHash, &config) - require.NoError(t, err) - - stringsCollected := []string{} - err = walk(&config, nil, func(_ []string, str string) (string, error) { - stringsCollected = append(stringsCollected, str) - return str + "_verified", nil - }) - require.NoError(t, err) - - sort.Strings(stringsCollected) - assert.Equal(t, []string{ - "1", - "2", - "test1", - "test2", - "test3", - "test4", - "test5", - }, stringsCollected) - - updatedConf, err := yaml.Marshal(config) - require.NoError(t, err) - assert.Equal(t, string(testYamlHashUpdated), string(updatedConf)) -} - -func TestDecryptNoCommand(t *testing.T) { +func TestResolveNoCommand(t *testing.T) { resolver := newEnabledSecretResolver() resolver.fetchHookFunc = func(secrets []string) (map[string]string, error) { return nil, fmt.Errorf("some error") } // since we didn't set any command this should return without any error - resConf, err := resolver.Decrypt(testConf, "test") + resConf, err := resolver.Resolve(testConf, "test") require.NoError(t, err) assert.Equal(t, testConf, resConf) } -func TestDecryptSecretError(t *testing.T) { +func TestResolveSecretError(t *testing.T) { resolver := newEnabledSecretResolver() resolver.backendCommand = "some_command" @@ -235,15 +206,15 @@ func TestDecryptSecretError(t *testing.T) { return nil, fmt.Errorf("some error") } - _, err := resolver.Decrypt(testConf, "test") + _, err := resolver.Resolve(testConf, "test") require.NotNil(t, err) } -func TestDecrypt(t *testing.T) { +func TestResolve(t *testing.T) { type testCase struct { name string testConf []byte - decryptedConf string + resolveedConf string expectedSecretOrigin handleToContext expectedScrubbedKey []string secretFetchCB func([]string) (map[string]string, error) @@ -253,13 +224,13 @@ func TestDecrypt(t *testing.T) { currentTest := t testCases := []testCase{ { - // TestDecryptSecretStringMapStringWithDashValue checks that a nested string config value + // TestResolveSecretStringMapStringWithDashValue checks that a nested string config value // that can be interpreted as YAML (such as a "-") is not interpreted as YAML by the secrets // decryption logic, but is left unchanged as a string instead. // See https://github.com/DataDog/datadog-agent/pull/6586 for details. name: "map with dash value", testConf: testConfDash, - decryptedConf: testConfDecryptedDash, + resolveedConf: testConfResolveedDash, expectedSecretOrigin: testConfDashOrigin, expectedScrubbedKey: []string{"some_encoded_password"}, secretFetchCB: func(secrets []string) (map[string]string, error) { @@ -275,7 +246,7 @@ func TestDecrypt(t *testing.T) { { name: "multiline", testConf: testConfMultiline, - decryptedConf: testConfDecryptedMultiline, + resolveedConf: testConfResolveedMultiline, expectedSecretOrigin: testConfMultilineOrigin, expectedScrubbedKey: []string{"some_encoded_password"}, secretFetchCB: func(secrets []string) (map[string]string, error) { @@ -291,7 +262,7 @@ func TestDecrypt(t *testing.T) { { name: "nested", testConf: testConfNested, - decryptedConf: testConfDecryptedNested, + resolveedConf: testConfResolveedNested, expectedSecretOrigin: testConfNestedOrigin, expectedScrubbedKey: []string{"data"}, secretFetchCB: func(secrets []string) (map[string]string, error) { @@ -307,7 +278,7 @@ func TestDecrypt(t *testing.T) { { name: "no cache", testConf: testConf, - decryptedConf: testConfDecrypted, + resolveedConf: testConfResolveed, expectedSecretOrigin: testConfOrigin, expectedScrubbedKey: []string{"password", "password"}, secretFetchCB: func(secrets []string) (map[string]string, error) { @@ -326,7 +297,7 @@ func TestDecrypt(t *testing.T) { { name: "partial cache", testConf: testConf, - decryptedConf: testConfDecrypted, + resolveedConf: testConfResolveed, expectedSecretOrigin: testConfOrigin, expectedScrubbedKey: []string{"password", "password"}, secretCache: map[string]string{"pass1": "password1"}, @@ -344,7 +315,7 @@ func TestDecrypt(t *testing.T) { { name: "full cache", testConf: testConf, - decryptedConf: testConfDecrypted, + resolveedConf: testConfResolveed, expectedSecretOrigin: testConfOrigin, expectedScrubbedKey: []string{"password", "password"}, secretCache: map[string]string{"pass1": "password1", "pass2": "password2"}, @@ -368,12 +339,56 @@ func TestDecrypt(t *testing.T) { scrubbedKey := []string{} resolver.scrubHookFunc = func(k []string) { scrubbedKey = append(scrubbedKey, k[0]) } - newConf, err := resolver.Decrypt(tc.testConf, "test") + newConf, err := resolver.Resolve(tc.testConf, "test") require.NoError(t, err) - assert.Equal(t, tc.decryptedConf, string(newConf)) + assert.Equal(t, tc.resolveedConf, string(newConf)) assert.Equal(t, tc.expectedSecretOrigin, resolver.origin) assert.Equal(t, tc.expectedScrubbedKey, scrubbedKey) }) } } + +func TestResolveWithCallback(t *testing.T) { + testConf := testConfNestedMultiple + + resolver := newEnabledSecretResolver() + resolver.backendCommand = "some_command" + resolver.cache = map[string]string{"pass3": "password3"} + + resolver.fetchHookFunc = func(secrets []string) (map[string]string, error) { + return map[string]string{ + "pass1": "password1", + "pass2": "password2", + }, nil + } + + topLevelResolved := 0 + secondLevelResolved := 0 + thirdLevelResolved := 0 + err := resolver.ResolveWithCallback( + testConf, + "test", + func(yamlPath []string, value any) { + switch strings.Join(yamlPath, "/") { + case "top_level": + assert.Equal(t, "password1", value) + topLevelResolved++ + case "some/second_level": + assert.Equal(t, "password2", value) + secondLevelResolved++ + case "some/encoded/third_level": + assert.Equal(t, "password3", value) + thirdLevelResolved++ + default: + assert.Fail(t, "unknown yaml path: %s", yamlPath) + } + }, + ) + require.NoError(t, err) + assert.Equal(t, 1, topLevelResolved, "'top_level' secret was not resolved or resolved multiple times") + assert.Equal(t, 1, secondLevelResolved, "'second_level' secret was not resolved or resolved multiple times") + assert.Equal(t, 1, thirdLevelResolved, "'third_level' secret was not resolved or resolved multiple times") + + assert.Equal(t, testConfNestedOriginMultiple, resolver.origin) +} diff --git a/comp/core/secrets/secretsimpl/walker.go b/comp/core/secrets/secretsimpl/walker.go new file mode 100644 index 0000000000000..49a39584de09d --- /dev/null +++ b/comp/core/secrets/secretsimpl/walker.go @@ -0,0 +1,114 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package secretsimpl + +import ( + "github.com/DataDog/datadog-agent/comp/core/secrets" +) + +type resolverCallback func([]string, string) (string, error) + +type walker struct { + // resolver is called to fetch the value of a handle + resolver resolverCallback + // notifier is called each time a key from the YAML is updated. This is used by ResolveWithCallback. + // + // When a slice is updated, this will be called once with the final slice content. + notifier secrets.ResolveCallback +} + +func (w *walker) notify(yamlPath []string, value any) { + if w.notifier != nil { + w.notifier(yamlPath, value) + } +} + +func (w *walker) string(yamlPath []string, value string, shouldNotify bool) (string, error) { + newValue, err := w.resolver(yamlPath, value) + if err != nil { + return value, err + } + + if shouldNotify && value != newValue { + w.notify(yamlPath, newValue) + } + return newValue, err +} + +func (w *walker) slice(data []interface{}, yamlPath []string) error { + var shouldNotify bool + for idx, k := range data { + switch v := k.(type) { + case string: + if newValue, err := w.string(yamlPath, v, false); err == nil { + if v != newValue { + data[idx] = newValue + shouldNotify = true + } + } else { + return err + } + case map[interface{}]interface{}: + if err := w.hash(v, yamlPath); err != nil { + return err + } + case []interface{}: + if err := w.slice(v, yamlPath); err != nil { + return err + } + } + } + // for slice we notify once with the final values + if shouldNotify { + w.notify(yamlPath, data) + } + return nil +} + +func (w *walker) hash(data map[interface{}]interface{}, yamlPath []string) error { + for k := range data { + path := yamlPath + if newkey, ok := k.(string); ok { + path = append(path, newkey) + } + + switch v := data[k].(type) { + case string: + if newValue, err := w.string(path, v, true); err == nil { + data[k] = newValue + } else { + return err + } + case map[interface{}]interface{}: + if err := w.hash(v, path); err != nil { + return err + } + case []interface{}: + if err := w.slice(v, path); err != nil { + return err + } + } + } + return nil +} + +// walk will go through loaded yaml and invoke the callback on every string node allowing +// the callback to overwrite the string value +func (w *walker) walk(data *interface{}) error { + switch v := (*data).(type) { + case string: + if newValue, err := w.string(nil, v, true); err == nil { + *data = newValue + } else { + return err + } + case map[interface{}]interface{}: + return w.hash(v, nil) + case []interface{}: + return w.slice(v, nil) + } + return nil +} diff --git a/comp/core/secrets/secretsimpl/walker_test.go b/comp/core/secrets/secretsimpl/walker_test.go new file mode 100644 index 0000000000000..e347bf0360b0d --- /dev/null +++ b/comp/core/secrets/secretsimpl/walker_test.go @@ -0,0 +1,134 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package secretsimpl + +import ( + "fmt" + "sort" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + yaml "gopkg.in/yaml.v2" +) + +func TestWalkerError(t *testing.T) { + var config interface{} + err := yaml.Unmarshal(testYamlHash, &config) + require.NoError(t, err) + + w := walker{ + resolver: func([]string, string) (string, error) { + return "", fmt.Errorf("some error") + }, + } + + err = w.walk(&config) + assert.NotNil(t, err) +} + +func TestWalkerSimple(t *testing.T) { + var config interface{} + err := yaml.Unmarshal([]byte("test"), &config) + require.NoError(t, err) + + stringsCollected := []string{} + + w := walker{ + resolver: func(_ []string, str string) (string, error) { + stringsCollected = append(stringsCollected, str) + return str + "_verified", nil + }, + } + err = w.walk(&config) + require.NoError(t, err) + + assert.Equal(t, []string{"test"}, stringsCollected) + + updatedConf, err := yaml.Marshal(config) + require.NoError(t, err) + assert.Equal(t, string("test_verified\n"), string(updatedConf)) +} + +func TestWalkerComplex(t *testing.T) { + var config interface{} + err := yaml.Unmarshal(testYamlHash, &config) + require.NoError(t, err) + + stringsCollected := []string{} + w := walker{ + resolver: func(_ []string, str string) (string, error) { + stringsCollected = append(stringsCollected, str) + return str + "_verified", nil + }, + } + err = w.walk(&config) + require.NoError(t, err) + + sort.Strings(stringsCollected) + assert.Equal(t, []string{ + "1", + "2", + "test1", + "test2", + "test3", + "test4", + "test5", + }, stringsCollected) + + updatedConf, err := yaml.Marshal(config) + require.NoError(t, err) + assert.Equal(t, string(testYamlHashUpdated), string(updatedConf)) +} + +func TestWalkerNotify(t *testing.T) { + yamlConf := []byte(` +slice: + - "ENC 1" + - ["ENC test1", test2] + - 123 +hash: + a: test3 + b: "ENC 2" + c: 456 + slice: + - ENC test4 + - test5 +`) + + notification := map[string]any{} + w := walker{ + resolver: func(_ []string, value string) (string, error) { + if strings.HasPrefix(value, "ENC ") { + return value[4:] + "_verified", nil + } + return value, nil + }, + notifier: func(yamlPath []string, value any) { + notification[strings.Join(yamlPath, ".")] = value + }, + } + + var config interface{} + err := yaml.Unmarshal(yamlConf, &config) + require.NoError(t, err) + + err = w.walk(&config) + require.NoError(t, err) + + // we expect to be notified once for each updated value (especially a single call per slice) + expected := map[string]any{ + "hash.b": "2_verified", + "hash.slice": []any{"test4_verified", "test5"}, + "slice": []any{ + "1_verified", + []any{"test1_verified", "test2"}, + 123, + }, + } + assert.Equal(t, expected, notification) +} diff --git a/comp/core/secrets/type.go b/comp/core/secrets/type.go index 72f07c8389b72..7602578e9c390 100644 --- a/comp/core/secrets/type.go +++ b/comp/core/secrets/type.go @@ -11,5 +11,8 @@ type SecretVal struct { ErrorMsg string `json:"error,omitempty"` } +// ResolveCallback is the callback type used by the ResolveWithCallback method +type ResolveCallback func(key []string, value any) + // PayloadVersion defines the current payload version sent to a secret backend const PayloadVersion = "1.0" diff --git a/pkg/autodiscovery/secrets.go b/pkg/autodiscovery/secrets.go index 3f6735f107922..6db09d295809f 100644 --- a/pkg/autodiscovery/secrets.go +++ b/pkg/autodiscovery/secrets.go @@ -23,7 +23,7 @@ func decryptConfig(conf integration.Config, secretResolver secrets.Component) (i var err error // init_config - conf.InitConfig, err = secretResolver.Decrypt(conf.InitConfig, conf.Name) + conf.InitConfig, err = secretResolver.Resolve(conf.InitConfig, conf.Name) if err != nil { return conf, fmt.Errorf("error while decrypting secrets in 'init_config': %s", err) } @@ -32,7 +32,7 @@ func decryptConfig(conf integration.Config, secretResolver secrets.Component) (i // we cannot update in place as, being a slice, it would modify the input config as well instances := make([]integration.Data, 0, len(conf.Instances)) for _, inputInstance := range conf.Instances { - decryptedInstance, err := secretResolver.Decrypt(inputInstance, conf.Name) + decryptedInstance, err := secretResolver.Resolve(inputInstance, conf.Name) if err != nil { return conf, fmt.Errorf("error while decrypting secrets in an instance: %s", err) } @@ -41,13 +41,13 @@ func decryptConfig(conf integration.Config, secretResolver secrets.Component) (i conf.Instances = instances // metrics - conf.MetricConfig, err = secretResolver.Decrypt(conf.MetricConfig, conf.Name) + conf.MetricConfig, err = secretResolver.Resolve(conf.MetricConfig, conf.Name) if err != nil { return conf, fmt.Errorf("error while decrypting secrets in 'metrics': %s", err) } // logs - conf.LogsConfig, err = secretResolver.Decrypt(conf.LogsConfig, conf.Name) + conf.LogsConfig, err = secretResolver.Resolve(conf.LogsConfig, conf.Name) if err != nil { return conf, fmt.Errorf("error while decrypting secrets 'logs': %s", err) } diff --git a/pkg/autodiscovery/secrets_test.go b/pkg/autodiscovery/secrets_test.go index b6590e9cdd2e0..e1eb4dc01c741 100644 --- a/pkg/autodiscovery/secrets_test.go +++ b/pkg/autodiscovery/secrets_test.go @@ -38,7 +38,7 @@ func (m *MockSecretResolver) Configure(_ string, _ []string, _, _ int, _, _ bool func (m *MockSecretResolver) GetDebugInfo(_ io.Writer) {} -func (m *MockSecretResolver) Decrypt(data []byte, origin string) ([]byte, error) { +func (m *MockSecretResolver) Resolve(data []byte, origin string) ([]byte, error) { if m.scenarios == nil { return data, nil } @@ -48,8 +48,12 @@ func (m *MockSecretResolver) Decrypt(data []byte, origin string) ([]byte, error) return scenario.returnedData, scenario.returnedError } } - m.t.Errorf("Decrypt called with unexpected arguments: data=%s, origin=%s", string(data), origin) - return nil, fmt.Errorf("Decrypt called with unexpected arguments: data=%s, origin=%s", string(data), origin) + m.t.Errorf("Resolve called with unexpected arguments: data=%s, origin=%s", string(data), origin) + return nil, fmt.Errorf("Resolve called with unexpected arguments: data=%s, origin=%s", string(data), origin) +} + +func (m *MockSecretResolver) ResolveWithCallback(_ []byte, _ string, _ secrets.ResolveCallback) error { + return nil } func (m *MockSecretResolver) haveAllScenariosBeenCalled() bool { @@ -111,29 +115,29 @@ var makeSharedScenarios = func() []mockSecretScenario { } } -func TestSecretDecrypt(t *testing.T) { - mockDecrypt := &MockSecretResolver{t, makeSharedScenarios()} +func TestSecretResolve(t *testing.T) { + mockResolve := &MockSecretResolver{t, makeSharedScenarios()} - newConfig, err := decryptConfig(sharedTpl, mockDecrypt) + newConfig, err := decryptConfig(sharedTpl, mockResolve) require.NoError(t, err) assert.NotEqual(t, newConfig.Instances, sharedTpl.Instances) - assert.True(t, mockDecrypt.haveAllScenariosBeenCalled()) + assert.True(t, mockResolve.haveAllScenariosBeenCalled()) } -func TestSkipSecretDecrypt(t *testing.T) { - mockDecrypt := &MockSecretResolver{t, makeSharedScenarios()} +func TestSkipSecretResolve(t *testing.T) { + mockResolve := &MockSecretResolver{t, makeSharedScenarios()} cfg := config.Mock(t) cfg.SetWithoutSource("secret_backend_skip_checks", true) defer cfg.SetWithoutSource("secret_backend_skip_checks", false) - c, err := decryptConfig(sharedTpl, mockDecrypt) + c, err := decryptConfig(sharedTpl, mockResolve) require.NoError(t, err) assert.Equal(t, sharedTpl.Instances, c.Instances) assert.Equal(t, sharedTpl.InitConfig, c.InitConfig) - assert.True(t, mockDecrypt.haveAllScenariosNotCalled()) + assert.True(t, mockResolve.haveAllScenariosNotCalled()) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 48f3bf34e3b19..2bc47036604d6 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -7,7 +7,6 @@ package config import ( - "bytes" "context" "encoding/json" "errors" @@ -1774,14 +1773,17 @@ func ResolveSecrets(config Config, secretResolver secrets.Component, origin stri return fmt.Errorf("unable to marshal configuration to YAML to decrypt secrets: %v", err) } - finalYamlConf, err := secretResolver.Decrypt(yamlConf, origin) + err = secretResolver.ResolveWithCallback( + yamlConf, + origin, + func(yamlPath []string, value any) { + settingName := strings.Join(yamlPath, ".") + log.Debugf("replacing handle for setting '%s' with secret value", settingName) + config.Set(settingName, value, pkgconfigmodel.SourceAgentRuntime) + }) if err != nil { return fmt.Errorf("unable to decrypt secret from datadog.yaml: %v", err) } - r := bytes.NewReader(finalYamlConf) - if err = config.MergeConfigOverride(r); err != nil { - return fmt.Errorf("could not update main configuration after decrypting secrets: %v", err) - } } return nil } diff --git a/pkg/config/config_secret_test.go b/pkg/config/config_secret_test.go index e178d662f36fa..d010371076b2c 100644 --- a/pkg/config/config_secret_test.go +++ b/pkg/config/config_secret_test.go @@ -28,10 +28,9 @@ func TestProxyWithSecret(t *testing.T) { { name: "secrets from configuration for proxy", setup: func(t *testing.T, config Config, resolver *secretsimpl.MockSecretResolver) { - resolver.Inject("http_handle", "http_url") - resolver.Inject("https_handle", "https_url") - resolver.Inject("no_proxy_1_handle", "no_proxy_1") - resolver.Inject("no_proxy_2_handle", "no_proxy_2") + resolver.InjectCallback([]string{"proxy", "http"}, "http_url") + resolver.InjectCallback([]string{"proxy", "https"}, "https_url") + resolver.InjectCallback([]string{"proxy", "no_proxy"}, []string{"no_proxy_1", "no_proxy_2"}) config.SetWithoutSource("secret_backend_command", "some_command") config.SetWithoutSource("proxy.http", "ENC[http_handle]") @@ -51,10 +50,9 @@ func TestProxyWithSecret(t *testing.T) { { name: "secrets fron DD env vars for proxy", setup: func(t *testing.T, config Config, resolver *secretsimpl.MockSecretResolver) { - resolver.Inject("http_handle", "http_url") - resolver.Inject("https_handle", "https_url") - resolver.Inject("no_proxy_1_handle", "no_proxy_1") - resolver.Inject("no_proxy_2_handle", "no_proxy_2") + resolver.InjectCallback([]string{"proxy", "http"}, "http_url") + resolver.InjectCallback([]string{"proxy", "https"}, "https_url") + resolver.InjectCallback([]string{"proxy", "no_proxy"}, []string{"no_proxy_1", "no_proxy_2"}) config.SetWithoutSource("secret_backend_command", "some_command") t.Setenv("DD_PROXY_HTTP", "ENC[http_handle]") @@ -74,10 +72,9 @@ func TestProxyWithSecret(t *testing.T) { { name: "secrets fron UNIX env vars for proxy", setup: func(t *testing.T, config Config, resolver *secretsimpl.MockSecretResolver) { - resolver.Inject("http_handle", "http_url") - resolver.Inject("https_handle", "https_url") - resolver.Inject("no_proxy_1_handle", "no_proxy_1") - resolver.Inject("no_proxy_2_handle", "no_proxy_2") + resolver.InjectCallback([]string{"proxy", "http"}, "http_url") + resolver.InjectCallback([]string{"proxy", "https"}, "https_url") + resolver.InjectCallback([]string{"proxy", "no_proxy"}, []string{"no_proxy_1", "no_proxy_2"}) config.SetWithoutSource("secret_backend_command", "some_command") t.Setenv("HTTP_PROXY", "ENC[http_handle]") From 0d7557ad17f4c65d928ee3312746955c26f8c72e Mon Sep 17 00:00:00 2001 From: Dinesh Gurumurthy Date: Wed, 29 Nov 2023 12:37:28 -0500 Subject: [PATCH 77/87] Create comp/core/secrets module (#21098) * Convert pkg/util/winutil to module * create pkg/secrets module * Fix compiliation issues * go tidy * fix gofmt * remove un-needed files * add doc file * fix lint issues * Add deleted files back * deleted un-needed * PR feedback update codeowners * fix typo * apply suggestion for doc.go * Fix revive error * Fix compilation error * Fix revive error that I missed * Update test to handle new mod * fix lint + test run * remove pkg/secrets * Create comp/core/secrets module * add module * revert fx upgrade --------- Co-authored-by: Julien Lebot --- comp/core/flare/types/go.mod | 13 ++++++ comp/core/flare/types/go.sum | 24 ++++++++++ comp/core/secrets/go.mod | 59 +++++++++++++++++++++++ comp/core/secrets/go.sum | 91 ++++++++++++++++++++++++++++++++++++ go.mod | 4 ++ tasks/modules.py | 2 + 6 files changed, 193 insertions(+) create mode 100644 comp/core/flare/types/go.mod create mode 100644 comp/core/flare/types/go.sum create mode 100644 comp/core/secrets/go.mod create mode 100644 comp/core/secrets/go.sum diff --git a/comp/core/flare/types/go.mod b/comp/core/flare/types/go.mod new file mode 100644 index 0000000000000..815937c4a8444 --- /dev/null +++ b/comp/core/flare/types/go.mod @@ -0,0 +1,13 @@ +module github.com/DataDog/datadog-agent/comp/core/flare/types + +go 1.20 + +require go.uber.org/fx v1.18.2 + +require ( + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/dig v1.17.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.23.0 // indirect + golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect +) diff --git a/comp/core/flare/types/go.sum b/comp/core/flare/types/go.sum new file mode 100644 index 0000000000000..9342228bdb644 --- /dev/null +++ b/comp/core/flare/types/go.sum @@ -0,0 +1,24 @@ +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= +go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= +go.uber.org/fx v1.18.2 h1:bUNI6oShr+OVFQeU8cDNbnN7VFsu+SsjHzUF51V/GAU= +go.uber.org/fx v1.18.2/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/comp/core/secrets/go.mod b/comp/core/secrets/go.mod new file mode 100644 index 0000000000000..0f6270f0348f3 --- /dev/null +++ b/comp/core/secrets/go.mod @@ -0,0 +1,59 @@ +module github.com/DataDog/datadog-agent/comp/core/secrets + +go 1.20 + +replace ( + github.com/DataDog/datadog-agent/comp/core/flare/types => ../flare/types + github.com/DataDog/datadog-agent/pkg/telemetry => ../../../pkg/telemetry + github.com/DataDog/datadog-agent/pkg/util/fxutil => ../../../pkg/util/fxutil + github.com/DataDog/datadog-agent/pkg/util/log => ../../../pkg/util/log + github.com/DataDog/datadog-agent/pkg/util/scrubber => ../../../pkg/util/scrubber + github.com/DataDog/datadog-agent/pkg/util/winutil => ../../../pkg/util/winutil + +) + +require ( + github.com/DataDog/datadog-agent/comp/core/flare/types v0.0.0-00010101000000-000000000000 + github.com/DataDog/datadog-agent/pkg/telemetry v0.0.0-00010101000000-000000000000 + github.com/DataDog/datadog-agent/pkg/util/fxutil v0.50.0-rc.4 + github.com/DataDog/datadog-agent/pkg/util/log v0.0.0-00010101000000-000000000000 + github.com/DataDog/datadog-agent/pkg/util/scrubber v0.50.0-rc.4 + github.com/DataDog/datadog-agent/pkg/util/winutil v0.0.0-00010101000000-000000000000 + github.com/stretchr/testify v1.8.4 + go.uber.org/fx v1.18.2 + golang.org/x/sys v0.14.0 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/DataDog/datadog-agent/comp/core/telemetry v0.50.0-rc.4 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.11.1 // indirect + github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + go.opentelemetry.io/otel v1.20.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.42.0 // indirect + go.opentelemetry.io/otel/metric v1.20.0 // indirect + go.opentelemetry.io/otel/sdk v1.20.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.20.0 // indirect + go.opentelemetry.io/otel/trace v1.20.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/dig v1.17.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.23.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/comp/core/secrets/go.sum b/comp/core/secrets/go.sum new file mode 100644 index 0000000000000..c367cfa710928 --- /dev/null +++ b/comp/core/secrets/go.sum @@ -0,0 +1,91 @@ +github.com/DataDog/datadog-agent/comp/core/telemetry v0.50.0-rc.4 h1:otuQe2x+WAgN78POmUSlOq0MRJkoLRlyGR5Fn3H55fI= +github.com/DataDog/datadog-agent/comp/core/telemetry v0.50.0-rc.4/go.mod h1:fqxtze7NiJ+jQnLCT/F38ewDy8yVGraReiJNZRtQNVM= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc= +go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0 h1:jwV9iQdvp38fxXi8ZC+lNpxjK16MRcZlpDYvbuO1FiA= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0/go.mod h1:f3bYiqNqhoPxkvI2LrXqQVC546K7BuRDL/kKuxkujhA= +go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA= +go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM= +go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlAWGM= +go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0= +go.opentelemetry.io/otel/sdk/metric v1.20.0 h1:5eD40l/H2CqdKmbSV7iht2KMK0faAIL2pVYzJOWobGk= +go.opentelemetry.io/otel/sdk/metric v1.20.0/go.mod h1:AGvpC+YF/jblITiafMTYgvRBUiwi9hZf0EYE2E5XlS8= +go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ= +go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= +go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= +go.uber.org/fx v1.18.2 h1:bUNI6oShr+OVFQeU8cDNbnN7VFsu+SsjHzUF51V/GAU= +go.uber.org/fx v1.18.2/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.mod b/go.mod index 6ba699b70d071..8190335db181c 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,8 @@ replace ( ) replace ( + github.com/DataDog/datadog-agent/comp/core/flare/types => ./comp/core/flare/types + github.com/DataDog/datadog-agent/comp/core/secrets => ./comp/core/secrets github.com/DataDog/datadog-agent/comp/core/telemetry => ./comp/core/telemetry/ github.com/DataDog/datadog-agent/pkg/aggregator/ckey => ./pkg/aggregator/ckey/ github.com/DataDog/datadog-agent/pkg/collector/check/defaults => ./pkg/collector/check/defaults @@ -584,6 +586,8 @@ require github.com/lorenzosaino/go-sysctl v0.3.1 require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/DataDog/agent-payload/v5 v5.0.100 + github.com/DataDog/datadog-agent/comp/core/flare/types v0.0.0-00010101000000-000000000000 + github.com/DataDog/datadog-agent/comp/core/secrets v0.0.0-00010101000000-000000000000 github.com/DataDog/datadog-agent/comp/core/telemetry v0.50.0-rc.4 github.com/DataDog/datadog-agent/pkg/aggregator/ckey v0.50.0-rc.4 github.com/DataDog/datadog-agent/pkg/collector/check/defaults v0.50.0-rc.4 diff --git a/tasks/modules.py b/tasks/modules.py index 47a89de08251f..47430c9030b6e 100644 --- a/tasks/modules.py +++ b/tasks/modules.py @@ -160,6 +160,8 @@ def dependency_path(self, agent_version): "pkg/tagset": GoModule("pkg/tagset", independent=True), "pkg/metrics": GoModule("pkg/metrics", independent=True), "pkg/telemetry": GoModule("pkg/telemetry", independent=True), + "comp/core/flare/types": GoModule("comp/core/flare/types", independent=True), + "comp/core/secrets": GoModule("comp/core/secrets", independent=True), "comp/core/telemetry": GoModule("comp/core/telemetry", independent=True), "pkg/config/model": GoModule("pkg/config/model", independent=True), "pkg/config/env": GoModule("pkg/config/env", independent=True), From 6df74f0fd3383298a80bd44da2b6ddfe2cc5e19c Mon Sep 17 00:00:00 2001 From: Nicolas Schweitzer Date: Wed, 29 Nov 2023 19:48:27 +0100 Subject: [PATCH 78/87] Change deleted jira project (#21182) Change deleted jira project --- tasks/libs/pipeline_notifications.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/libs/pipeline_notifications.py b/tasks/libs/pipeline_notifications.py index eb078ee38edca..41081f47f58e4 100644 --- a/tasks/libs/pipeline_notifications.py +++ b/tasks/libs/pipeline_notifications.py @@ -67,7 +67,7 @@ "@datadog/windows-agent": "WINA", "@datadog/windows-kernel-integrations": "WKINT", "@datadog/opentelemetry": "OTEL", - "@datadog/agent-e2e-testing": "AETT", + "@datadog/agent-e2e-testing": "APL", "@datadog/software-integrity-and-trust": "SINT", "@datadog/single-machine-performance": "SMP", "@datadog/agent-integrations": "AI", From 9e0e095564ec091fa53e75450e8cfa4e5c522bb5 Mon Sep 17 00:00:00 2001 From: Kylian Serrania Date: Wed, 29 Nov 2023 20:13:56 +0100 Subject: [PATCH 79/87] Revert "Merge software definitions for integration installation for py2 and py3 (#21041)" (#21184) This reverts commit c58154604454c79cee30085cc5fc58268150d004. It broke Agent 6 builds on ARM Linux. --- .../create-regex-at-runtime.patch | 0 .../remove-maxfile-maxpath-psutil.patch | 0 .../remove-maxfile-maxpath-psutil.patch | 30 + omnibus/config/projects/agent.rb | 6 +- .../datadog-agent-integrations-py2.rb | 460 +++++++++++++++ .../datadog-agent-integrations-py3.rb | 490 ++++++++++++++++ .../software/datadog-agent-integrations.rb | 524 ------------------ .../static_requirements.txt.erb | 0 .../static_requirements.txt.erb | 3 + 9 files changed, 986 insertions(+), 527 deletions(-) rename omnibus/config/patches/{datadog-agent-integrations => datadog-agent-integrations-py2}/create-regex-at-runtime.patch (100%) rename omnibus/config/patches/{datadog-agent-integrations => datadog-agent-integrations-py2}/remove-maxfile-maxpath-psutil.patch (100%) create mode 100644 omnibus/config/patches/datadog-agent-integrations-py3/remove-maxfile-maxpath-psutil.patch create mode 100644 omnibus/config/software/datadog-agent-integrations-py2.rb create mode 100644 omnibus/config/software/datadog-agent-integrations-py3.rb delete mode 100644 omnibus/config/software/datadog-agent-integrations.rb rename omnibus/config/templates/{datadog-agent-integrations => datadog-agent-integrations-py2}/static_requirements.txt.erb (100%) create mode 100644 omnibus/config/templates/datadog-agent-integrations-py3/static_requirements.txt.erb diff --git a/omnibus/config/patches/datadog-agent-integrations/create-regex-at-runtime.patch b/omnibus/config/patches/datadog-agent-integrations-py2/create-regex-at-runtime.patch similarity index 100% rename from omnibus/config/patches/datadog-agent-integrations/create-regex-at-runtime.patch rename to omnibus/config/patches/datadog-agent-integrations-py2/create-regex-at-runtime.patch diff --git a/omnibus/config/patches/datadog-agent-integrations/remove-maxfile-maxpath-psutil.patch b/omnibus/config/patches/datadog-agent-integrations-py2/remove-maxfile-maxpath-psutil.patch similarity index 100% rename from omnibus/config/patches/datadog-agent-integrations/remove-maxfile-maxpath-psutil.patch rename to omnibus/config/patches/datadog-agent-integrations-py2/remove-maxfile-maxpath-psutil.patch diff --git a/omnibus/config/patches/datadog-agent-integrations-py3/remove-maxfile-maxpath-psutil.patch b/omnibus/config/patches/datadog-agent-integrations-py3/remove-maxfile-maxpath-psutil.patch new file mode 100644 index 0000000000000..c92eb2f7f2859 --- /dev/null +++ b/omnibus/config/patches/datadog-agent-integrations-py3/remove-maxfile-maxpath-psutil.patch @@ -0,0 +1,30 @@ +Partially reverts https://github.com/giampaolo/psutil/pull/1863 to remove the maxpath / maxfile fetch +diff --git a/psutil/__init__.py b/psutil/__init__.py +index 1a113bc3..ce962a61 100644 +--- a/psutil/__init__.py ++++ b/psutil/__init__.py +@@ -2012,23 +2012,7 @@ def disk_partitions(all=False): + If *all* parameter is False return physical devices only and ignore + all others. + """ +- def pathconf(path, name): +- try: +- return os.pathconf(path, name) +- except (OSError, AttributeError): +- pass +- +- ret = _psplatform.disk_partitions(all) +- if POSIX: +- new = [] +- for item in ret: +- nt = item._replace( +- maxfile=pathconf(item.mountpoint, 'PC_NAME_MAX'), +- maxpath=pathconf(item.mountpoint, 'PC_PATH_MAX')) +- new.append(nt) +- return new +- else: +- return ret ++ return _psplatform.disk_partitions(all) + + + def disk_io_counters(perdisk=False, nowrap=True): \ No newline at end of file diff --git a/omnibus/config/projects/agent.rb b/omnibus/config/projects/agent.rb index 6b77898b6d3f2..2384e20f0cd00 100644 --- a/omnibus/config/projects/agent.rb +++ b/omnibus/config/projects/agent.rb @@ -182,13 +182,13 @@ if with_python_runtime? "2" dependency 'pylint2' + dependency 'datadog-agent-integrations-py2' end -if with_python_runtime? "3" or with_python_runtime? "2" - dependency 'datadog-agent-integrations' +if with_python_runtime? "3" + dependency 'datadog-agent-integrations-py3' end - if linux_target? dependency 'datadog-security-agent-policies' end diff --git a/omnibus/config/software/datadog-agent-integrations-py2.rb b/omnibus/config/software/datadog-agent-integrations-py2.rb new file mode 100644 index 0000000000000..ff62baa97b2cf --- /dev/null +++ b/omnibus/config/software/datadog-agent-integrations-py2.rb @@ -0,0 +1,460 @@ +# Unless explicitly stated otherwise all files in this repository are licensed +# under the Apache License Version 2.0. +# This product includes software developed at Datadog (https:#www.datadoghq.com/). +# Copyright 2016-present Datadog, Inc. + +require './lib/ostools.rb' +require 'json' + +name 'datadog-agent-integrations-py2' + +license "BSD-3-Clause" +license_file "./LICENSE" + +dependency 'datadog-agent' +dependency 'datadog-agent-integrations-py2-dependencies' + +relative_path 'integrations-core' +whitelist_file "embedded/lib/python2.7/site-packages/.libsaerospike" +whitelist_file "embedded/lib/python2.7/site-packages/psycopg2" +whitelist_file "embedded/lib/python2.7/site-packages/wrapt" +whitelist_file "embedded/lib/python2.7/site-packages/pymqi" + +source git: 'https://github.com/DataDog/integrations-core.git' + +integrations_core_version = ENV['INTEGRATIONS_CORE_VERSION'] +if integrations_core_version.nil? || integrations_core_version.empty? + integrations_core_version = 'master' +end +default_version integrations_core_version + +# folder names containing integrations from -core that won't be packaged with the Agent +excluded_folders = [ + 'datadog_checks_base', # namespacing package for wheels (NOT AN INTEGRATION) + 'datadog_checks_dev', # Development package, (NOT AN INTEGRATION) + 'datadog_checks_tests_helper', # Testing and Development package, (NOT AN INTEGRATION) + 'docker_daemon', # Agent v5 only +] + +# package names of dependencies that won't be added to the Agent Python environment +excluded_packages = Array.new + + +if suse_target? + # Temporarily exclude Aerospike until builder supports new dependency + excluded_packages.push(/^aerospike==/) + excluded_folders.push('aerospike') +end + +if osx_target? + # exclude aerospike, new version 3.10 is not supported on MacOS yet + excluded_folders.push('aerospike') + + # Temporarily exclude Aerospike until builder supports new dependency + excluded_packages.push(/^aerospike==/) + excluded_folders.push('aerospike') +end + +if arm_target? + # Temporarily exclude Aerospike until builder supports new dependency + excluded_folders.push('aerospike') + excluded_packages.push(/^aerospike==/) + + # This doesn't build on ARM + excluded_folders.push('ibm_mq') + excluded_packages.push(/^pymqi==/) +end + +if arm_target? || !_64_bit? + excluded_packages.push(/^orjson==/) +end + +if linux_target? + excluded_packages.push(/^pyyaml==/) + excluded_packages.push(/^kubernetes==/) +end + +final_constraints_file = 'final_constraints-py2.txt' +agent_requirements_file = 'agent_requirements-py2.txt' +filtered_agent_requirements_in = 'agent_requirements-py2.in' +agent_requirements_in = 'agent_requirements.in' + +build do + # The dir for confs + if osx_target? + conf_dir = "#{install_dir}/etc/conf.d" + else + conf_dir = "#{install_dir}/etc/datadog-agent/conf.d" + end + mkdir conf_dir + + # aliases for pip + if windows_target? + pip = "#{windows_safe_path(python_2_embedded)}\\Scripts\\pip.exe" + python = "#{windows_safe_path(python_2_embedded)}\\python.exe" + else + pip = "#{install_dir}/embedded/bin/pip2" + python = "#{install_dir}/embedded/bin/python2" + end + + # If a python_mirror was set, it's passed through a pip config file so that we're not leaking the API key in the CI Output + # Else the pip config file so pip will act casually + pip_config_file = ENV['PIP_CONFIG_FILE'] + pre_build_env = { + "PIP_CONFIG_FILE" => "#{pip_config_file}" + } + + # Install the checks along with their dependencies + if windows_target? + wheel_build_dir = "#{windows_safe_path(project_dir)}\\.wheels" + build_deps_dir = "#{windows_safe_path(project_dir)}\\.build_deps" + else + wheel_build_dir = "#{project_dir}/.wheels" + build_deps_dir = "#{project_dir}/.build_deps" + end + + # + # Prepare the build env, these dependencies are only needed to build and + # install the core integrations. + # + command "#{pip} download --dest #{build_deps_dir} hatchling==0.25.1", :env => pre_build_env + command "#{pip} download --dest #{build_deps_dir} setuptools==40.9.0", :env => pre_build_env # Version from ./setuptools2.rb + command "#{pip} install wheel==0.37.1", :env => pre_build_env # Pin to the last version that supports Python 2 + command "#{pip} install setuptools-scm==5.0.2", :env => pre_build_env # Pin to the last version that supports Python 2 + command "#{pip} install pip-tools==5.4.0", :env => pre_build_env + uninstall_buildtime_deps = ['rtloader', 'click', 'first', 'pip-tools'] + nix_build_env = { + "PIP_FIND_LINKS" => "#{build_deps_dir}", + "PIP_CONFIG_FILE" => "#{pip_config_file}", + "CFLAGS" => "-I#{install_dir}/embedded/include -I/opt/mqm/inc", + "CXXFLAGS" => "-I#{install_dir}/embedded/include -I/opt/mqm/inc", + "LDFLAGS" => "-L#{install_dir}/embedded/lib -L/opt/mqm/lib64 -L/opt/mqm/lib", + "LD_RUN_PATH" => "#{install_dir}/embedded/lib -L/opt/mqm/lib64 -L/opt/mqm/lib", + "PATH" => "#{install_dir}/embedded/bin:#{ENV['PATH']}", + } + win_build_env = { + "PIP_FIND_LINKS" => "#{build_deps_dir}", + "PIP_CONFIG_FILE" => "#{pip_config_file}", + } + # Some libraries (looking at you, aerospike-client-python) need EXT_CFLAGS instead of CFLAGS. + nix_specific_build_env = { + "aerospike" => nix_build_env.merge({"EXT_CFLAGS" => nix_build_env["CFLAGS"] + " -std=gnu99"}), + # Always build pyodbc from source to link to the embedded version of libodbc + "pyodbc" => nix_build_env.merge({"PIP_NO_BINARY" => "pyodbc"}), + } + win_specific_build_env = {} + + + # On Linux & Windows, specify the C99 standard explicitly to avoid issues while building some + # wheels (eg. ddtrace). + # Not explicitly setting that option has caused us problems in the past on SUSE, where the ddtrace + # wheel has to be manually built, as the C code in ddtrace doesn't follow the C89 standard (the default value of std). + # Note: We don't set this on MacOS, as on MacOS we need to build a bunch of packages & C extensions that + # don't have precompiled MacOS wheels. When building C extensions, the CFLAGS variable is added to + # the command-line parameters, even when compiling C++ code, where -std=c99 is invalid. + # See: https://github.com/python/cpython/blob/v2.7.18/Lib/distutils/sysconfig.py#L222 + if linux_target? || windows_target? + nix_build_env["CFLAGS"] += " -std=c99" + end + + # + # Prepare the requirements file containing ALL the dependencies needed by + # any integration. This will provide the "static Python environment" of the Agent. + # We don't use the .in file provided by the base check directly because we + # want to filter out things before installing. + # + if windows_target? + static_reqs_in_file = "#{windows_safe_path(project_dir)}\\datadog_checks_base\\datadog_checks\\base\\data\\#{agent_requirements_in}" + static_reqs_out_folder = "#{windows_safe_path(project_dir)}\\" + static_reqs_out_file = static_reqs_out_folder + filtered_agent_requirements_in + compiled_reqs_file_path = "#{windows_safe_path(install_dir)}\\#{agent_requirements_file}" + else + static_reqs_in_file = "#{project_dir}/datadog_checks_base/datadog_checks/base/data/#{agent_requirements_in}" + static_reqs_out_folder = "#{project_dir}/" + static_reqs_out_file = static_reqs_out_folder + filtered_agent_requirements_in + compiled_reqs_file_path = "#{install_dir}/#{agent_requirements_file}" + end + + specific_build_env = windows_target? ? win_specific_build_env : nix_specific_build_env + build_env = windows_target? ? win_build_env : nix_build_env + cwd = windows_target? ? "#{windows_safe_path(project_dir)}\\datadog_checks_base" : "#{project_dir}/datadog_checks_base" + + # Creating a hash containing the requirements and requirements file path associated to every lib + requirements_custom = Hash.new() + specific_build_env.each do |lib, env| + lib_compiled_req_file_path = (windows_target? ? "#{windows_safe_path(install_dir)}\\" : "#{install_dir}/") + "agent_#{lib}_requirements-py2.txt" + requirements_custom[lib] = { + "req_lines" => Array.new, + "req_file_path" => static_reqs_out_folder + lib + "-py2.in", + "compiled_req_file_path" => lib_compiled_req_file_path, + } + end + + # Remove any excluded requirements from the static-environment req file + requirements = Array.new + + block "Create filtered requirements" do + File.open("#{static_reqs_in_file}", 'r+').readlines().each do |line| + next if excluded_packages.any? { |package_regex| line.match(package_regex) } + + if line.start_with?('psycopg[binary]') && !windows_target? + line.sub! 'psycopg[binary]', 'psycopg[c]' + end + # Keeping the custom env requirements lines apart to install them with a specific env + requirements_custom.each do |lib, lib_req| + if Regexp.new('^' + lib + '==').freeze.match line + lib_req["req_lines"].push(line) + end + end + # In any case we add the lib to the requirements files to avoid inconsistency in the installed versions + # For example if aerospike has dependency A>1.2.3 and a package in the big requirements file has A<1.2.3, the install process would succeed but the integration wouldn't work. + requirements.push(line) + end + + # Adding pympler for memory debug purposes + requirements.push("pympler==0.7") + + end + + # Render the filtered requirements file + erb source: "static_requirements.txt.erb", + dest: "#{static_reqs_out_file}", + mode: 0640, + vars: { requirements: requirements } + + # Render the filtered libraries that are to be built with different env var + requirements_custom.each do |lib, lib_req| + erb source: "static_requirements.txt.erb", + dest: "#{lib_req["req_file_path"]}", + mode: 0640, + vars: { requirements: lib_req["req_lines"] } + end + + # Increasing pip max retries (default: 5 times) and pip timeout (default 15 seconds) to avoid blocking network errors + pip_max_retries = 20 + pip_timeout = 20 + + # Use pip-compile to create the final requirements file. Notice when we invoke `pip` through `python -m pip <...>`, + # there's no need to refer to `pip`, the interpreter will pick the right script. + command "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => build_env, :cwd => cwd + command "#{python} -m pip install datadog_checks_base --no-deps --no-index --find-links=#{wheel_build_dir}" + command "#{python} -m piptools compile --generate-hashes --output-file #{compiled_reqs_file_path} #{static_reqs_out_file} " \ + "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => build_env + # Pip-compiling seperately each lib that needs a custom build installation + specific_build_env.each do |lib, env| + command "#{python} -m piptools compile --generate-hashes --output-file #{requirements_custom[lib]["compiled_req_file_path"]} #{requirements_custom[lib]["req_file_path"]} " \ + "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => env + end + + # + # Install static-environment requirements that the Agent and all checks will use + # + + # First we install the dependencies that need specific flags + specific_build_env.each do |lib, env| + command "#{python} -m pip install --no-deps --require-hashes -r #{requirements_custom[lib]["compiled_req_file_path"]}", :env => env + # Remove the file after use so it is not shipped + delete "#{requirements_custom[lib]["compiled_req_file_path"]}" + end + + # Then we install the rest (already installed libraries will be ignored) with the main flags + command "#{python} -m pip install --no-deps --require-hashes -r #{compiled_reqs_file_path}", :env => build_env + # Remove the file after use so it is not shipped + delete "#{compiled_reqs_file_path}" + + # + # Install Core integrations + # + + # Create a constraint file after installing all the core dependencies and before any integration + # This is then used as a constraint file by the integration command to avoid messing with the agent's python environment + command "#{pip} freeze > #{install_dir}/#{final_constraints_file}" + + if windows_target? + cached_wheels_dir = "#{windows_safe_path(wheel_build_dir)}\\.cached" + else + cached_wheels_dir = "#{wheel_build_dir}/.cached" + end + + checks_to_install = Array.new + + block "Collect integrations to install" do + # Go through every integration package in `integrations-core`, build and install + Dir.glob("#{project_dir}/*").each do |check_dir| + check = check_dir.split('/').last + + # do not install excluded integrations + next if !File.directory?("#{check_dir}") || excluded_folders.include?(check) + + # If there is no manifest file, then we should assume the folder does not + # contain a working check and move onto the next + manifest_file_path = "#{check_dir}/manifest.json" + + # If there is no manifest file, then we should assume the folder does not + # contain a working check and move onto the next + File.exist?(manifest_file_path) || next + + manifest = JSON.parse(File.read(manifest_file_path)) + if manifest.key?("supported_os") + manifest["supported_os"].include?(os) || next + else + if os == "mac_os" + tag = "Supported OS::macOS" + else + tag = "Supported OS::#{os.capitalize}" + end + + manifest["tile"]["classifier_tags"].include?(tag) || next + end + + File.file?("#{check_dir}/setup.py") || File.file?("#{check_dir}/pyproject.toml") || next + # Check if it supports Python 2. + support = `inv agent.check-supports-python-version #{check_dir} 2` + if support == "False" + log.info(log_key) { "Skipping '#{check}' since it does not support Python 2." } + next + end + + checks_to_install.push(check) + end + end + + installed_list = Array.new + cache_bucket = ENV.fetch('INTEGRATION_WHEELS_CACHE_BUCKET', '') + block "Install integrations" do + tasks_dir_in = windows_safe_path(Dir.pwd) + cache_branch = (shellout! "inv release.get-release-json-value base_branch", cwd: File.expand_path('..', tasks_dir_in)).stdout.strip + # On windows, `aws` actually executes Ruby's AWS SDK, but we want the Python one + awscli = if windows_target? then '"c:\Program files\python39\scripts\aws"' else 'aws' end + if cache_bucket != '' + mkdir cached_wheels_dir + shellout! "inv -e agent.get-integrations-from-cache " \ + "--python 2 --bucket #{cache_bucket} " \ + "--branch #{cache_branch || 'main'} " \ + "--integrations-dir #{windows_safe_path(project_dir)} " \ + "--target-dir #{cached_wheels_dir} " \ + "--integrations #{checks_to_install.join(',')} " \ + "--awscli #{awscli}", + :cwd => tasks_dir_in + + # install all wheels from cache in one pip invocation to speed things up + if windows_target? + shellout! "#{python} -m pip install --no-deps --no-index " \ + "--find-links #{windows_safe_path(cached_wheels_dir)} -r #{windows_safe_path(cached_wheels_dir)}\\found.txt" + else + shellout! "#{pip} install --no-deps --no-index " \ + " --find-links #{cached_wheels_dir} -r #{cached_wheels_dir}/found.txt" + end + end + + # get list of integration wheels already installed from cache + if cache_bucket != '' + if windows_target? + installed_out = (shellout! "#{python} -m pip list --format json").stdout + else + installed_out = (shellout! "#{pip} list --format json").stdout + end + if $?.exitstatus == 0 + installed = JSON.parse(installed_out) + installed.each do |package| + package.each do |key, value| + if key == "name" && value.start_with?("datadog-") + installed_list.push(value["datadog-".length..-1]) + end + end + end + else + raise "Failed to list pip installed packages" + end + end + + checks_to_install.each do |check| + check_dir = File.join(project_dir, check) + check_conf_dir = "#{conf_dir}/#{check}.d" + + # For each conf file, if it already exists, that means the `datadog-agent` software def + # wrote it first. In that case, since the agent's confs take precedence, skip the conf + conf_files = ["conf.yaml.example", "conf.yaml.default", "metrics.yaml", "auto_conf.yaml"] + + conf_files.each do |filename| + src = windows_safe_path(check_dir,"datadog_checks", check, "data", filename) + dest = check_conf_dir + if File.exist?(src) and !File.exist?(windows_safe_path(dest, filename)) + FileUtils.mkdir_p(dest) + FileUtils.cp_r(src, dest) + end + end + + # Copy SNMP profiles + profile_folders = ['profiles', 'default_profiles'] + profile_folders.each do |profile_folder| + folder_path = "#{check_dir}/datadog_checks/#{check}/data/#{profile_folder}" + if File.exist? folder_path + FileUtils.cp_r folder_path, "#{check_conf_dir}/" + end + end + + # pip < 21.2 replace underscores by dashes in package names per https://pip.pypa.io/en/stable/news/#v21-2 + # whether or not this might switch back in the future is not guaranteed, so we check for both name + # with dashes and underscores + if installed_list.include?(check) || installed_list.include?(check.gsub('_', '-')) + next + end + + if windows_target? + shellout! "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => win_build_env, :cwd => "#{windows_safe_path(project_dir)}\\#{check}" + shellout! "#{python} -m pip install datadog-#{check} --no-deps --no-index --find-links=#{wheel_build_dir}" + else + shellout! "#{pip} wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => nix_build_env, :cwd => "#{project_dir}/#{check}" + shellout! "#{pip} install datadog-#{check} --no-deps --no-index --find-links=#{wheel_build_dir}" + end + if cache_bucket != '' && ENV.fetch('INTEGRATION_WHEELS_SKIP_CACHE_UPLOAD', '') == '' && cache_branch != nil + shellout! "inv -e agent.upload-integration-to-cache " \ + "--python 2 --bucket #{cache_bucket} " \ + "--branch #{cache_branch} " \ + "--integrations-dir #{windows_safe_path(project_dir)} " \ + "--build-dir #{wheel_build_dir} " \ + "--integration #{check} " \ + "--awscli #{awscli}", + :cwd => tasks_dir_in + end + end + end + + # From now on we don't need piptools anymore, uninstall its deps so we don't include them in the final artifact + uninstall_buildtime_deps.each do |dep| + if windows_target? + command "#{python} -m pip uninstall -y #{dep}" + else + command "#{pip} uninstall -y #{dep}" + end + end + + # Patch applies to only one file: set it explicitly as a target, no need for -p + if windows_target? + patch :source => "create-regex-at-runtime.patch", :target => "#{python_2_embedded}/Lib/site-packages/yaml/reader.py" + patch :source => "remove-maxfile-maxpath-psutil.patch", :target => "#{python_2_embedded}/Lib/site-packages/psutil/__init__.py" + else + patch :source => "create-regex-at-runtime.patch", :target => "#{install_dir}/embedded/lib/python2.7/site-packages/yaml/reader.py" + patch :source => "remove-maxfile-maxpath-psutil.patch", :target => "#{install_dir}/embedded/lib/python2.7/site-packages/psutil/__init__.py" + end + + # Run pip check to make sure the agent's python environment is clean, all the dependencies are compatible + if windows_target? + command "#{python} -m pip check" + else + command "#{pip} check" + end + + # Removing tests that don't need to be shipped in the embedded folder + if windows_target? + delete "#{python_2_embedded}/Lib/site-packages/Cryptodome/SelfTest/" + else + delete "#{install_dir}/embedded/lib/python2.7/site-packages/Cryptodome/SelfTest/" + end + + # Ship `requirements-agent-release.txt` file containing the versions of every check shipped with the agent + # Used by the `datadog-agent integration` command to prevent downgrading a check to a version + # older than the one shipped in the agent + copy "#{project_dir}/requirements-agent-release.txt", "#{install_dir}/" +end diff --git a/omnibus/config/software/datadog-agent-integrations-py3.rb b/omnibus/config/software/datadog-agent-integrations-py3.rb new file mode 100644 index 0000000000000..ffb437079370d --- /dev/null +++ b/omnibus/config/software/datadog-agent-integrations-py3.rb @@ -0,0 +1,490 @@ +# Unless explicitly stated otherwise all files in this repository are licensed +# under the Apache License Version 2.0. +# This product includes software developed at Datadog (https:#www.datadoghq.com/). +# Copyright 2016-present Datadog, Inc. + +require './lib/ostools.rb' +require 'json' + +name 'datadog-agent-integrations-py3' + +license "BSD-3-Clause" +license_file "./LICENSE" + +dependency 'datadog-agent' +dependency 'datadog-agent-integrations-py3-dependencies' + +relative_path 'integrations-core' +whitelist_file "embedded/lib/python3.11/site-packages/.libsaerospike" +whitelist_file "embedded/lib/python3.11/site-packages/aerospike.libs" +whitelist_file "embedded/lib/python3.11/site-packages/psycopg2" +whitelist_file "embedded/lib/python3.11/site-packages/pymqi" + +source git: 'https://github.com/DataDog/integrations-core.git' + +gcc_version = ENV['GCC_VERSION'] +if gcc_version.nil? || gcc_version.empty? + gcc_version = '10.4.0' +end + +integrations_core_version = ENV['INTEGRATIONS_CORE_VERSION'] +if integrations_core_version.nil? || integrations_core_version.empty? + integrations_core_version = 'master' +end +default_version integrations_core_version + +# folder names containing integrations from -core that won't be packaged with the Agent +excluded_folders = [ + 'datadog_checks_base', # namespacing package for wheels (NOT AN INTEGRATION) + 'datadog_checks_dev', # Development package, (NOT AN INTEGRATION) + 'datadog_checks_tests_helper', # Testing and Development package, (NOT AN INTEGRATION) + 'docker_daemon', # Agent v5 only +] + +# package names of dependencies that won't be added to the Agent Python environment +excluded_packages = Array.new + +# We build these manually +excluded_packages.push(/^snowflake-connector-python==/) +excluded_packages.push(/^confluent-kafka==/) + +if suse_target? + # Temporarily exclude Aerospike until builder supports new dependency + excluded_packages.push(/^aerospike==/) + excluded_folders.push('aerospike') +end + +if osx_target? + # Temporarily exclude Aerospike until builder supports new dependency + excluded_packages.push(/^aerospike==/) + excluded_folders.push('aerospike') + excluded_folders.push('teradata') +end + +if arm_target? + # This doesn't build on ARM + excluded_folders.push('ibm_ace') + excluded_folders.push('ibm_mq') + excluded_packages.push(/^pymqi==/) +end + +# We explicitly check for redhat builder, not target +# Our centos/redhat builder uses glibc 2.12 while pydantic +# requires glibc 2.17 +if redhat? && !arm_target? + excluded_packages.push(/^pydantic-core==/) +end + +# _64_bit checks the kernel arch. On windows, the builder is 64 bit +# even when doing a 32 bit build. Do a specific check for the 32 bit +# build +if arm_target? || !_64_bit? || (windows_target? && windows_arch_i386?) + excluded_packages.push(/^orjson==/) +end + +if linux_target? + excluded_packages.push(/^oracledb==/) +end + +final_constraints_file = 'final_constraints-py3.txt' +agent_requirements_file = 'agent_requirements-py3.txt' +filtered_agent_requirements_in = 'agent_requirements-py3.in' +agent_requirements_in = 'agent_requirements.in' + +build do + # The dir for confs + if osx_target? + conf_dir = "#{install_dir}/etc/conf.d" + else + conf_dir = "#{install_dir}/etc/datadog-agent/conf.d" + end + mkdir conf_dir + + # aliases for pip + if windows_target? + python = "#{windows_safe_path(python_3_embedded)}\\python.exe" + else + python = "#{install_dir}/embedded/bin/python3" + end + + # If a python_mirror is set, it is set in a pip config file so that we do not leak the token in the CI output + pip_config_file = ENV['PIP_CONFIG_FILE'] + pre_build_env = { + "PIP_CONFIG_FILE" => "#{pip_config_file}" + } + + # Install the checks along with their dependencies + if windows_target? + wheel_build_dir = "#{windows_safe_path(project_dir)}\\.wheels" + build_deps_dir = "#{windows_safe_path(project_dir)}\\.build_deps" + else + wheel_build_dir = "#{project_dir}/.wheels" + build_deps_dir = "#{project_dir}/.build_deps" + end + + # + # Prepare the build env, these dependencies are only needed to build and + # install the core integrations. + # + command "#{python} -m pip download --dest #{build_deps_dir} hatchling==0.25.1", :env => pre_build_env + command "#{python} -m pip download --dest #{build_deps_dir} setuptools==66.1.1", :env => pre_build_env # Version from ./setuptools3.rb + command "#{python} -m pip install wheel==0.38.4", :env => pre_build_env + command "#{python} -m pip install pip-tools==7.3.0", :env => pre_build_env + uninstall_buildtime_deps = ['rtloader', 'click', 'first', 'pip-tools'] + nix_build_env = { + "PIP_FIND_LINKS" => "#{build_deps_dir}", + "PIP_CONFIG_FILE" => "#{pip_config_file}", + # Specify C99 standard explicitly to avoid issues while building some + # wheels (eg. ddtrace) + "CFLAGS" => "-I#{install_dir}/embedded/include -I/opt/mqm/inc", + "CXXFLAGS" => "-I#{install_dir}/embedded/include -I/opt/mqm/inc", + "LDFLAGS" => "-L#{install_dir}/embedded/lib -L/opt/mqm/lib64 -L/opt/mqm/lib", + "LD_RUN_PATH" => "#{install_dir}/embedded/lib -L/opt/mqm/lib64 -L/opt/mqm/lib", + "PATH" => "#{install_dir}/embedded/bin:#{ENV['PATH']}", + } + + win_build_env = { + "PIP_FIND_LINKS" => "#{build_deps_dir}", + "PIP_CONFIG_FILE" => "#{pip_config_file}", + } + + # Some libraries (looking at you, aerospike-client-python) need EXT_CFLAGS instead of CFLAGS. + nix_specific_build_env = { + "aerospike" => nix_build_env.merge({"EXT_CFLAGS" => nix_build_env["CFLAGS"] + " -std=gnu99"}), + # Always build pyodbc from source to link to the embedded version of libodbc + "pyodbc" => nix_build_env.merge({"PIP_NO_BINARY" => "pyodbc"}), + } + win_specific_build_env = {} + + # On Linux & Windows, specify the C99 standard explicitly to avoid issues while building some + # wheels (eg. ddtrace). + # Not explicitly setting that option has caused us problems in the past on SUSE, where the ddtrace + # wheel has to be manually built, as the C code in ddtrace doesn't follow the C89 standard (the default value of std). + # Note: We don't set this on MacOS, as on MacOS we need to build a bunch of packages & C extensions that + # don't have precompiled MacOS wheels. When building C extensions, the CFLAGS variable is added to + # the command-line parameters, even when compiling C++ code, where -std=c99 is invalid. + # See: https://github.com/python/cpython/blob/v3.8.8/Lib/distutils/sysconfig.py#L227 + if linux_target? || windows_target? + nix_build_env["CFLAGS"] += " -std=c99" + end + + # We only have gcc 10.4.0 on linux for now + if linux_target? + nix_build_env["CC"] = "/opt/gcc-#{gcc_version}/bin/gcc" + nix_build_env["CXX"] = "/opt/gcc-#{gcc_version}/bin/g++" + end + + # We need to explicitly specify RUSTFLAGS for libssl and libcrypto + # See https://github.com/pyca/cryptography/issues/8614#issuecomment-1489366475 + if redhat_target? && !arm_target? + nix_specific_build_env["cryptography"] = nix_build_env.merge( + { + "RUSTFLAGS" => "-C link-arg=-Wl,-rpath,#{install_dir}/embedded/lib", + "OPENSSL_DIR" => "#{install_dir}/embedded/", + # We have a manually installed dependency (snowflake connector) that already installed cryptography (but without the flags) + # We force reinstall it from source to be sure we use the flag + "PIP_NO_CACHE_DIR" => "off", + "PIP_FORCE_REINSTALL" => "1", + } + ) + end + + # + # Prepare the requirements file containing ALL the dependencies needed by + # any integration. This will provide the "static Python environment" of the Agent. + # We don't use the .in file provided by the base check directly because we + # want to filter out things before installing. + # + if windows_target? + static_reqs_in_file = "#{windows_safe_path(project_dir)}\\datadog_checks_base\\datadog_checks\\base\\data\\#{agent_requirements_in}" + static_reqs_out_folder = "#{windows_safe_path(project_dir)}\\" + static_reqs_out_file = static_reqs_out_folder + filtered_agent_requirements_in + compiled_reqs_file_path = "#{windows_safe_path(install_dir)}\\#{agent_requirements_file}" + else + static_reqs_in_file = "#{project_dir}/datadog_checks_base/datadog_checks/base/data/#{agent_requirements_in}" + static_reqs_out_folder = "#{project_dir}/" + static_reqs_out_file = static_reqs_out_folder + filtered_agent_requirements_in + compiled_reqs_file_path = "#{install_dir}/#{agent_requirements_file}" + end + + specific_build_env = windows_target? ? win_specific_build_env : nix_specific_build_env + build_env = windows_target? ? win_build_env : nix_build_env + cwd_base = windows_target? ? "#{windows_safe_path(project_dir)}\\datadog_checks_base" : "#{project_dir}/datadog_checks_base" + cwd_downloader = windows_target? ? "#{windows_safe_path(project_dir)}\\datadog_checks_downloader" : "#{project_dir}/datadog_checks_downloader" + + # Creating a hash containing the requirements and requirements file path associated to every lib + requirements_custom = Hash.new() + specific_build_env.each do |lib, env| + lib_compiled_req_file_path = (windows_target? ? "#{windows_safe_path(install_dir)}\\" : "#{install_dir}/") + "agent_#{lib}_requirements-py3.txt" + requirements_custom[lib] = { + "req_lines" => Array.new, + "req_file_path" => static_reqs_out_folder + lib + "-py3.in", + "compiled_req_file_path" => lib_compiled_req_file_path, + } + end + + # Remove any excluded requirements from the static-environment req file + requirements = Array.new + + block "Create filtered requirements" do + File.open("#{static_reqs_in_file}", 'r+').readlines().each do |line| + next if excluded_packages.any? { |package_regex| line.match(package_regex) } + + # on non windows OS, we use the c version of the psycopg installation + if line.start_with?('psycopg[binary]') && !windows_target? + line.sub! 'psycopg[binary]', 'psycopg[c]' + end + # Keeping the custom env requirements lines apart to install them with a specific env + requirements_custom.each do |lib, lib_req| + if Regexp.new('^' + lib + '==').freeze.match line + lib_req["req_lines"].push(line) + end + end + # In any case we add the lib to the requirements files to avoid inconsistency in the installed versions + # For example if aerospike has dependency A>1.2.3 and a package in the big requirements file has A<1.2.3, the install process would succeed but the integration wouldn't work. + requirements.push(line) + end + + # Adding pympler for memory debug purposes + requirements.push("pympler==0.7") + end + + # Render the filtered requirements file + erb source: "static_requirements.txt.erb", + dest: "#{static_reqs_out_file}", + mode: 0640, + vars: { requirements: requirements } + + # Render the filtered libraries that are to be built with different env var + requirements_custom.each do |lib, lib_req| + erb source: "static_requirements.txt.erb", + dest: "#{lib_req["req_file_path"]}", + mode: 0640, + vars: { requirements: lib_req["req_lines"] } + end + + # Constraints file for constraining transitive dependencies in those cases where there may be incompatible versions + constraints = [] + if redhat_target? + constraints.push("bcrypt < 4.1.0") + end + + constraints_file = windows_safe_path(project_dir, "constraints.txt") + block "Write constraints file" do + File.open(constraints_file, 'w') { |f| f << constraints.join("\n") } + end + + + # Increasing pip max retries (default: 5 times) and pip timeout (default 15 seconds) to avoid blocking network errors + pip_max_retries = 20 + pip_timeout = 20 + + # Use pip-compile to create the final requirements file. Notice when we invoke `pip` through `python -m pip <...>`, + # there's no need to refer to `pip`, the interpreter will pick the right script. + command "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => build_env, :cwd => cwd_base + command "#{python} -m pip install datadog_checks_base --no-deps --no-index --find-links=#{wheel_build_dir}" + command "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => build_env, :cwd => cwd_downloader + command "#{python} -m pip install datadog_checks_downloader --no-deps --no-index --find-links=#{wheel_build_dir}" + command "#{python} -m piptools compile --generate-hashes -c #{constraints_file} --output-file #{compiled_reqs_file_path} #{static_reqs_out_file} " \ + "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => build_env + # Pip-compiling seperately each lib that needs a custom build installation + specific_build_env.each do |lib, env| + command "#{python} -m piptools compile --generate-hashes -c #{constraints_file} --output-file #{requirements_custom[lib]["compiled_req_file_path"]} #{requirements_custom[lib]["req_file_path"]} " \ + "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => env + end + + # + # Install static-environment requirements that the Agent and all checks will use + # + + # First we install the dependencies that need specific flags + specific_build_env.each do |lib, env| + command "#{python} -m pip install --no-deps --require-hashes -r #{requirements_custom[lib]["compiled_req_file_path"]}", :env => env + # Remove the file after use so it is not shipped + delete "#{requirements_custom[lib]["compiled_req_file_path"]}" + end + # Then we install the rest (already installed libraries will be ignored) with the main flags + command "#{python} -m pip install --no-deps --require-hashes -r #{compiled_reqs_file_path}", :env => build_env + # Remove the file after use so it is not shipped + delete "#{compiled_reqs_file_path}" + + # + # Install Core integrations + # + + # Create a constraint file after installing all the core dependencies and before any integration + # This is then used as a constraint file by the integration command to avoid messing with the agent's python environment + command "#{python} -m pip freeze > #{install_dir}/#{final_constraints_file}" + + if windows_target? + cached_wheels_dir = "#{windows_safe_path(wheel_build_dir)}\\.cached" + else + cached_wheels_dir = "#{wheel_build_dir}/.cached" + end + + checks_to_install = Array.new + + block "Collect integrations to install" do + # Go through every integration package in `integrations-core`, build and install + Dir.glob("#{project_dir}/*").each do |check_dir| + check = check_dir.split('/').last + + # do not install excluded integrations + next if !File.directory?("#{check_dir}") || excluded_folders.include?(check) + + # If there is no manifest file, then we should assume the folder does not + # contain a working check and move onto the next + manifest_file_path = "#{check_dir}/manifest.json" + + # If there is no manifest file, then we should assume the folder does not + # contain a working check and move onto the next + File.exist?(manifest_file_path) || next + + manifest = JSON.parse(File.read(manifest_file_path)) + if manifest.key?("supported_os") + manifest["supported_os"].include?(os) || next + else + if os == "mac_os" + tag = "Supported OS::macOS" + else + tag = "Supported OS::#{os.capitalize}" + end + + manifest["tile"]["classifier_tags"].include?(tag) || next + end + + File.file?("#{check_dir}/setup.py") || File.file?("#{check_dir}/pyproject.toml") || next + # Check if it supports Python 3. + support = `inv agent.check-supports-python-version #{check_dir} 3` + if support == "False" + log.info(log_key) { "Skipping '#{check}' since it does not support Python 3." } + next + end + + checks_to_install.push(check) + end + end + + installed_list = Array.new + cache_bucket = ENV.fetch('INTEGRATION_WHEELS_CACHE_BUCKET', '') + block "Install integrations" do + tasks_dir_in = windows_safe_path(Dir.pwd) + cache_branch = (shellout! "inv release.get-release-json-value base_branch", cwd: File.expand_path('..', tasks_dir_in)).stdout.strip + # On windows, `aws` actually executes Ruby's AWS SDK, but we want the Python one + awscli = if windows_target? then '"c:\Program files\python39\scripts\aws"' else 'aws' end + if cache_bucket != '' + mkdir cached_wheels_dir + shellout! "inv -e agent.get-integrations-from-cache " \ + "--python 3 --bucket #{cache_bucket} " \ + "--branch #{cache_branch || 'main'} " \ + "--integrations-dir #{windows_safe_path(project_dir)} " \ + "--target-dir #{cached_wheels_dir} " \ + "--integrations #{checks_to_install.join(',')} " \ + "--awscli #{awscli}", + :cwd => tasks_dir_in + + # install all wheels from cache in one pip invocation to speed things up + if windows_target? + shellout! "#{python} -m pip install --no-deps --no-index " \ + " --find-links #{windows_safe_path(cached_wheels_dir)} -r #{windows_safe_path(cached_wheels_dir)}\\found.txt" + else + shellout! "#{python} -m pip install --no-deps --no-index " \ + "--find-links #{cached_wheels_dir} -r #{cached_wheels_dir}/found.txt" + end + end + + # get list of integration wheels already installed from cache + if cache_bucket != '' + installed_out = (shellout! "#{python} -m pip list --format json").stdout + if $?.exitstatus == 0 + installed = JSON.parse(installed_out) + installed.each do |package| + package.each do |key, value| + if key == "name" && value.start_with?("datadog-") + installed_list.push(value["datadog-".length..-1]) + end + end + end + else + raise "Failed to list pip installed packages" + end + end + + checks_to_install.each do |check| + check_dir = File.join(project_dir, check) + check_conf_dir = "#{conf_dir}/#{check}.d" + + # For each conf file, if it already exists, that means the `datadog-agent` software def + # wrote it first. In that case, since the agent's confs take precedence, skip the conf + conf_files = ["conf.yaml.example", "conf.yaml.default", "metrics.yaml", "auto_conf.yaml"] + conf_files.each do |filename| + src = windows_safe_path(check_dir,"datadog_checks", check, "data", filename) + dest = check_conf_dir + if File.exist?(src) and !File.exist?(windows_safe_path(dest, filename)) + FileUtils.mkdir_p(dest) + FileUtils.cp_r(src, dest) + end + end + + # Copy SNMP profiles + profile_folders = ['profiles', 'default_profiles'] + profile_folders.each do |profile_folder| + folder_path = "#{check_dir}/datadog_checks/#{check}/data/#{profile_folder}" + if File.exist? folder_path + FileUtils.cp_r folder_path, "#{check_conf_dir}/" + end + end + + # pip < 21.2 replace underscores by dashes in package names per https://pip.pypa.io/en/stable/news/#v21-2 + # whether or not this might switch back in the future is not guaranteed, so we check for both name + # with dashes and underscores + if installed_list.include?(check) || installed_list.include?(check.gsub('_', '-')) + next + end + + if windows_target? + shellout! "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => win_build_env, :cwd => "#{windows_safe_path(project_dir)}\\#{check}" + else + shellout! "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => nix_build_env, :cwd => "#{project_dir}/#{check}" + end + shellout! "#{python} -m pip install datadog-#{check} --no-deps --no-index --find-links=#{wheel_build_dir}" + if cache_bucket != '' && ENV.fetch('INTEGRATION_WHEELS_SKIP_CACHE_UPLOAD', '') == '' && cache_branch != nil + shellout! "inv -e agent.upload-integration-to-cache " \ + "--python 3 --bucket #{cache_bucket} " \ + "--branch #{cache_branch} " \ + "--integrations-dir #{windows_safe_path(project_dir)} " \ + "--build-dir #{wheel_build_dir} " \ + "--integration #{check} " \ + "--awscli #{awscli}", + :cwd => tasks_dir_in + end + end + end + + # From now on we don't need piptools anymore, uninstall its deps so we don't include them in the final artifact + uninstall_buildtime_deps.each do |dep| + command "#{python} -m pip uninstall -y #{dep}" + end + + # Patch applies to only one file: set it explicitly as a target, no need for -p + if windows_target? + patch :source => "remove-maxfile-maxpath-psutil.patch", :target => "#{python_3_embedded}/Lib/site-packages/psutil/__init__.py" + else + patch :source => "remove-maxfile-maxpath-psutil.patch", :target => "#{install_dir}/embedded/lib/python3.11/site-packages/psutil/__init__.py" + end + + # Run pip check to make sure the agent's python environment is clean, all the dependencies are compatible + command "#{python} -m pip check" + + # Removing tests that don't need to be shipped in the embedded folder + if windows_target? + delete "#{python_3_embedded}/Lib/site-packages/Cryptodome/SelfTest/" + else + delete "#{install_dir}/embedded/lib/python3.11/site-packages/Cryptodome/SelfTest/" + end + + # Ship `requirements-agent-release.txt` file containing the versions of every check shipped with the agent + # Used by the `datadog-agent integration` command to prevent downgrading a check to a version + # older than the one shipped in the agent + copy "#{project_dir}/requirements-agent-release.txt", "#{install_dir}/" +end diff --git a/omnibus/config/software/datadog-agent-integrations.rb b/omnibus/config/software/datadog-agent-integrations.rb deleted file mode 100644 index fa21fe029973a..0000000000000 --- a/omnibus/config/software/datadog-agent-integrations.rb +++ /dev/null @@ -1,524 +0,0 @@ -# Unless explicitly stated otherwise all files in this repository are licensed -# under the Apache License Version 2.0. -# This product includes software developed at Datadog (https:#www.datadoghq.com/). -# Copyright 2016-present Datadog, Inc. - -require './lib/ostools.rb' -require 'json' - -name 'datadog-agent-integrations' - -license "BSD-3-Clause" -license_file "./LICENSE" - -source git: 'https://github.com/DataDog/integrations-core.git' -relative_path 'integrations-core' - -integrations_core_version = ENV['INTEGRATIONS_CORE_VERSION'] -if integrations_core_version.nil? || integrations_core_version.empty? - integrations_core_version = 'master' -end - -default_version integrations_core_version - -dependency 'datadog-agent' - -if with_python_runtime? "2" - dependency 'datadog-agent-integrations-py2-dependencies' - - whitelist_file "embedded/lib/python2.7/site-packages/.libsaerospike" - whitelist_file "embedded/lib/python2.7/site-packages/psycopg2" - whitelist_file "embedded/lib/python2.7/site-packages/wrapt" - whitelist_file "embedded/lib/python2.7/site-packages/pymqi" -end - -if with_python_runtime? "3" - dependency 'datadog-agent-integrations-py3-dependencies' - - whitelist_file "embedded/lib/python3.11/site-packages/.libsaerospike" - whitelist_file "embedded/lib/python3.11/site-packages/aerospike.libs" - whitelist_file "embedded/lib/python3.11/site-packages/psycopg2" - whitelist_file "embedded/lib/python3.11/site-packages/pymqi" -end - -gcc_version = ENV['GCC_VERSION'] -if gcc_version.nil? || gcc_version.empty? - gcc_version = '10.4.0' -end - -# folder names containing integrations from -core that won't be packaged with the Agent -excluded_folders = [ - 'datadog_checks_base', # namespacing package for wheels (NOT AN INTEGRATION) - 'datadog_checks_dev', # Development package, (NOT AN INTEGRATION) - 'datadog_checks_tests_helper', # Testing and Development package, (NOT AN INTEGRATION) - 'docker_daemon', # Agent v5 only -] - -# package names of dependencies that won't be added to the Agent Python environment -excluded_packages = Array.new - -# We build these manually -excluded_packages.push(/^snowflake-connector-python==/) -excluded_packages.push(/^confluent-kafka==/) - -if suse_target? - # Temporarily exclude Aerospike until builder supports new dependency - excluded_packages.push(/^aerospike==/) - excluded_folders.push('aerospike') -end - -if osx_target? - # Temporarily exclude Aerospike until builder supports new dependency - excluded_packages.push(/^aerospike==/) - excluded_folders.push('aerospike') - excluded_folders.push('teradata') -end - -if arm_target? - # This doesn't build on ARM - excluded_folders.push('ibm_ace') - excluded_folders.push('ibm_mq') - excluded_packages.push(/^pymqi==/) -end - -# We explicitly check for redhat builder, not target -# Our centos/redhat builder uses glibc 2.12 while pydantic -# requires glibc 2.17 -if redhat? && !arm_target? - excluded_packages.push(/^pydantic-core==/) -end - -# _64_bit checks the kernel arch. On windows, the builder is 64 bit -# even when doing a 32 bit build. Do a specific check for the 32 bit -# build -if arm_target? || !_64_bit? || (windows_target? && windows_arch_i386?) - excluded_packages.push(/^orjson==/) -end - -if linux_target? - excluded_packages.push(/^oracledb==/) -end - -build do - ["2", "3"].each do |python_major_version| - # Skip python version if not included in this build - next if not with_python_runtime?(python_major_version) - - final_constraints_file = "final_constraints-py#{python_major_version}.txt" - agent_requirements_file = "agent_requirements-py#{python_major_version}.txt" - filtered_agent_requirements_in = "agent_requirements-py#{python_major_version}.in" - agent_requirements_in = 'agent_requirements.in' - - # The dir for confs - if osx_target? - conf_dir = "#{install_dir}/etc/conf.d" - else - conf_dir = "#{install_dir}/etc/datadog-agent/conf.d" - end - mkdir conf_dir - - # aliases for pip - if windows_target? - python = windows_safe_path(install_dir, "embedded#{python_major_version}", "python.exe") - else - python = "#{install_dir}/embedded/bin/python#{python_major_version}" - end - - # If a python_mirror is set, it is set in a pip config file so that we do not leak the token in the CI output - pip_config_file = ENV['PIP_CONFIG_FILE'] - pre_build_env = { - "PIP_CONFIG_FILE" => "#{pip_config_file}" - } - - # Install the checks along with their dependencies - wheel_build_dir = windows_safe_path(project_dir, python_major_version, ".wheels") - build_deps_dir = windows_safe_path(project_dir, python_major_version, ".build_deps") - - # - # Prepare the build env, these dependencies are only needed to build and - # install the core integrations. - # - if python_major_version == "2" - command "#{python} -m pip download --dest #{build_deps_dir} hatchling==0.25.1", :env => pre_build_env - command "#{python} -m pip download --dest #{build_deps_dir} setuptools==40.9.0", :env => pre_build_env # Version from ./setuptools2.rb - command "#{python} -m pip install wheel==0.37.1", :env => pre_build_env # Pin to the last version that supports Python 2 - command "#{python} -m pip install setuptools-scm==5.0.2", :env => pre_build_env # Pin to the last version that supports Python 2 - command "#{python} -m pip install pip-tools==5.4.0", :env => pre_build_env - else - command "#{python} -m pip download --dest #{build_deps_dir} hatchling==0.25.1", :env => pre_build_env - command "#{python} -m pip download --dest #{build_deps_dir} setuptools==66.1.1", :env => pre_build_env # Version from ./setuptools3.rb - command "#{python} -m pip install wheel==0.38.4", :env => pre_build_env - command "#{python} -m pip install pip-tools==7.3.0", :env => pre_build_env - end - uninstall_buildtime_deps = ['rtloader', 'click', 'first', 'pip-tools'] - nix_build_env = { - "PIP_FIND_LINKS" => "#{build_deps_dir}", - "PIP_CONFIG_FILE" => "#{pip_config_file}", - "CFLAGS" => "-I#{install_dir}/embedded/include -I/opt/mqm/inc", - "CXXFLAGS" => "-I#{install_dir}/embedded/include -I/opt/mqm/inc", - "LDFLAGS" => "-L#{install_dir}/embedded/lib -L/opt/mqm/lib64 -L/opt/mqm/lib", - "LD_RUN_PATH" => "#{install_dir}/embedded/lib -L/opt/mqm/lib64 -L/opt/mqm/lib", - "PATH" => "#{install_dir}/embedded/bin:#{ENV['PATH']}", - } - - win_build_env = { - "PIP_FIND_LINKS" => "#{build_deps_dir}", - "PIP_CONFIG_FILE" => "#{pip_config_file}", - } - - # Some libraries (looking at you, aerospike-client-python) need EXT_CFLAGS instead of CFLAGS. - nix_specific_build_env = { - "aerospike" => nix_build_env.merge({"EXT_CFLAGS" => nix_build_env["CFLAGS"] + " -std=gnu99"}), - # Always build pyodbc from source to link to the embedded version of libodbc - "pyodbc" => nix_build_env.merge({"PIP_NO_BINARY" => "pyodbc"}), - } - win_specific_build_env = {} - - # On Linux & Windows, specify the C99 standard explicitly to avoid issues while building some - # wheels (eg. ddtrace). - # Not explicitly setting that option has caused us problems in the past on SUSE, where the ddtrace - # wheel has to be manually built, as the C code in ddtrace doesn't follow the C89 standard (the default value of std). - # Note: We don't set this on MacOS, as on MacOS we need to build a bunch of packages & C extensions that - # don't have precompiled MacOS wheels. When building C extensions, the CFLAGS variable is added to - # the command-line parameters, even when compiling C++ code, where -std=c99 is invalid. - # See: https://github.com/python/cpython/blob/v3.8.8/Lib/distutils/sysconfig.py#L227 - if linux_target? || windows_target? - nix_build_env["CFLAGS"] += " -std=c99" - end - - # We only have gcc 10.4.0 on linux for now - if linux_target? - nix_build_env["CC"] = "/opt/gcc-#{gcc_version}/bin/gcc" - nix_build_env["CXX"] = "/opt/gcc-#{gcc_version}/bin/g++" - end - - # We need to explicitly specify RUSTFLAGS for libssl and libcrypto - # See https://github.com/pyca/cryptography/issues/8614#issuecomment-1489366475 - if python_major_version == "3" && redhat_target? && !arm_target? - nix_specific_build_env["cryptography"] = nix_build_env.merge( - { - "RUSTFLAGS" => "-C link-arg=-Wl,-rpath,#{install_dir}/embedded/lib", - "OPENSSL_DIR" => "#{install_dir}/embedded/", - # We have a manually installed dependency (snowflake connector) that already installed cryptography (but without the flags) - # We force reinstall it from source to be sure we use the flag - "PIP_NO_CACHE_DIR" => "off", - "PIP_FORCE_REINSTALL" => "1", - } - ) - end - - # - # Prepare the requirements file containing ALL the dependencies needed by - # any integration. This will provide the "static Python environment" of the Agent. - # We don't use the .in file provided by the base check directly because we - # want to filter out things before installing. - # - if windows_target? - static_reqs_in_file = "#{windows_safe_path(project_dir)}\\datadog_checks_base\\datadog_checks\\base\\data\\#{agent_requirements_in}" - static_reqs_out_folder = "#{windows_safe_path(project_dir)}\\" - static_reqs_out_file = static_reqs_out_folder + filtered_agent_requirements_in - compiled_reqs_file_path = "#{windows_safe_path(install_dir)}\\#{agent_requirements_file}" - else - static_reqs_in_file = "#{project_dir}/datadog_checks_base/datadog_checks/base/data/#{agent_requirements_in}" - static_reqs_out_folder = "#{project_dir}/" - static_reqs_out_file = static_reqs_out_folder + filtered_agent_requirements_in - compiled_reqs_file_path = "#{install_dir}/#{agent_requirements_file}" - end - - specific_build_env = windows_target? ? win_specific_build_env : nix_specific_build_env - build_env = windows_target? ? win_build_env : nix_build_env - cwd_base = windows_target? ? "#{windows_safe_path(project_dir)}\\datadog_checks_base" : "#{project_dir}/datadog_checks_base" - cwd_downloader = windows_target? ? "#{windows_safe_path(project_dir)}\\datadog_checks_downloader" : "#{project_dir}/datadog_checks_downloader" - - # Creating a hash containing the requirements and requirements file path associated to every lib - requirements_custom = Hash.new() - specific_build_env.each do |lib, env| - lib_compiled_req_file_path = (windows_target? ? "#{windows_safe_path(install_dir)}\\" : "#{install_dir}/") + "agent_#{lib}_requirements-py3.txt" - requirements_custom[lib] = { - "req_lines" => Array.new, - "req_file_path" => static_reqs_out_folder + lib + "-py#{python_major_version}.in", - "compiled_req_file_path" => lib_compiled_req_file_path, - } - end - - # Remove any excluded requirements from the static-environment req file - requirements = Array.new - - block "Create filtered requirements" do - File.open("#{static_reqs_in_file}", 'r+').readlines().each do |line| - next if excluded_packages.any? { |package_regex| line.match(package_regex) } - - # on non windows OS, we use the c version of the psycopg installation - if line.start_with?('psycopg[binary]') && !windows_target? - line.sub! 'psycopg[binary]', 'psycopg[c]' - end - # Keeping the custom env requirements lines apart to install them with a specific env - requirements_custom.each do |lib, lib_req| - if Regexp.new('^' + lib + '==').freeze.match line - lib_req["req_lines"].push(line) - end - end - # In any case we add the lib to the requirements files to avoid inconsistency in the installed versions - # For example if aerospike has dependency A>1.2.3 and a package in the big requirements file has A<1.2.3, the install process would succeed but the integration wouldn't work. - requirements.push(line) - end - - # Adding pympler for memory debug purposes - requirements.push("pympler==0.7") - - end - - # Render the filtered requirements file - erb source: "static_requirements.txt.erb", - dest: "#{static_reqs_out_file}", - mode: 0640, - vars: { requirements: requirements } - - # Render the filtered libraries that are to be built with different env var - requirements_custom.each do |lib, lib_req| - erb source: "static_requirements.txt.erb", - dest: "#{lib_req["req_file_path"]}", - mode: 0640, - vars: { requirements: lib_req["req_lines"] } - end - - # Constraints file for constraining transitive dependencies in those cases where there may be incompatible versions (only supported for py3) - constraints_flag = "" - if python_major_version == "3" - constraints = [] - if redhat_target? - constraints.push("bcrypt < 4.1.0") - end - - constraints_file = windows_safe_path(project_dir, "constraints.txt") - block "Write constraints file" do - File.open(constraints_file, 'w') { |f| f << constraints.join("\n") } - end - constraints_flag = "-c #{constraints_file}" - end - - # Increasing pip max retries (default: 5 times) and pip timeout (default 15 seconds) to avoid blocking network errors - pip_max_retries = 20 - pip_timeout = 20 - - # Use pip-compile to create the final requirements file. Notice when we invoke `pip` through `python -m pip <...>`, - # there's no need to refer to `pip`, the interpreter will pick the right script. - command "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => build_env, :cwd => cwd_base - command "#{python} -m pip install datadog_checks_base --no-deps --no-index --find-links=#{wheel_build_dir}" - - # We only install the downloader on Python 3 - if python_major_version == "3" - command "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => build_env, :cwd => cwd_downloader - command "#{python} -m pip install datadog_checks_downloader --no-deps --no-index --find-links=#{wheel_build_dir}" - end - - command "#{python} -m piptools compile --generate-hashes #{constraints_flag} --output-file #{compiled_reqs_file_path} #{static_reqs_out_file} " \ - "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => build_env - # Pip-compiling seperately each lib that needs a custom build installation - specific_build_env.each do |lib, env| - command "#{python} -m piptools compile --generate-hashes #{constraints_flag} --output-file #{requirements_custom[lib]["compiled_req_file_path"]} #{requirements_custom[lib]["req_file_path"]} " \ - "--pip-args \"--retries #{pip_max_retries} --timeout #{pip_timeout}\"", :env => env - end - - # - # Install static-environment requirements that the Agent and all checks will use - # - - # First we install the dependencies that need specific flags - specific_build_env.each do |lib, env| - command "#{python} -m pip install --no-deps --require-hashes -r #{requirements_custom[lib]["compiled_req_file_path"]}", :env => env - # Remove the file after use so it is not shipped - delete "#{requirements_custom[lib]["compiled_req_file_path"]}" - end - # Then we install the rest (already installed libraries will be ignored) with the main flags - command "#{python} -m pip install --no-deps --require-hashes -r #{compiled_reqs_file_path}", :env => build_env - # Remove the file after use so it is not shipped - delete "#{compiled_reqs_file_path}" - - # - # Install Core integrations - # - - # Create a constraint file after installing all the core dependencies and before any integration - # This is then used as a constraint file by the integration command to avoid messing with the agent's python environment - command "#{python} -m pip freeze > #{install_dir}/#{final_constraints_file}" - - if windows_target? - cached_wheels_dir = "#{windows_safe_path(wheel_build_dir)}\\.cached" - else - cached_wheels_dir = "#{wheel_build_dir}/.cached" - end - - checks_to_install = Array.new - - block "Collect integrations to install" do - # Go through every integration package in `integrations-core`, build and install - Dir.glob("#{project_dir}/*").each do |check_dir| - check = check_dir.split('/').last - - # do not install excluded integrations - next if !File.directory?("#{check_dir}") || excluded_folders.include?(check) - - # If there is no manifest file, then we should assume the folder does not - # contain a working check and move onto the next - manifest_file_path = "#{check_dir}/manifest.json" - - # If there is no manifest file, then we should assume the folder does not - # contain a working check and move onto the next - File.exist?(manifest_file_path) || next - - manifest = JSON.parse(File.read(manifest_file_path)) - if manifest.key?("supported_os") - manifest["supported_os"].include?(os) || next - else - if os == "mac_os" - tag = "Supported OS::macOS" - else - tag = "Supported OS::#{os.capitalize}" - end - - manifest["tile"]["classifier_tags"].include?(tag) || next - end - - File.file?("#{check_dir}/setup.py") || File.file?("#{check_dir}/pyproject.toml") || next - # Check if it supports Python version we're building for. - support = `inv agent.check-supports-python-version #{check_dir} #{python_major_version}` - if support == "False" - log.info(log_key) { "Skipping '#{check}' since it does not support Python #{python_major_version}." } - next - end - - checks_to_install.push(check) - end - end - - installed_list = Array.new - cache_bucket = ENV.fetch('INTEGRATION_WHEELS_CACHE_BUCKET', '') - block "Install integrations" do - tasks_dir_in = windows_safe_path(Dir.pwd) - cache_branch = (shellout! "inv release.get-release-json-value base_branch", cwd: File.expand_path('..', tasks_dir_in)).stdout.strip - # On windows, `aws` actually executes Ruby's AWS SDK, but we want the Python one - awscli = if windows_target? then '"c:\Program files\python39\scripts\aws"' else 'aws' end - if cache_bucket != '' - mkdir cached_wheels_dir - shellout! "inv -e agent.get-integrations-from-cache " \ - "--python #{python_major_version} --bucket #{cache_bucket} " \ - "--branch #{cache_branch || 'main'} " \ - "--integrations-dir #{windows_safe_path(project_dir)} " \ - "--target-dir #{cached_wheels_dir} " \ - "--integrations #{checks_to_install.join(',')} " \ - "--awscli #{awscli}", - :cwd => tasks_dir_in - - # install all wheels from cache in one pip invocation to speed things up - if windows_target? - shellout! "#{python} -m pip install --no-deps --no-index " \ - " --find-links #{windows_safe_path(cached_wheels_dir)} -r #{windows_safe_path(cached_wheels_dir)}\\found.txt" - else - shellout! "#{python} -m pip install --no-deps --no-index " \ - "--find-links #{cached_wheels_dir} -r #{cached_wheels_dir}/found.txt" - end - end - - # get list of integration wheels already installed from cache - if cache_bucket != '' - installed_out = (shellout! "#{python} -m pip list --format json").stdout - if $?.exitstatus == 0 - installed = JSON.parse(installed_out) - installed.each do |package| - package.each do |key, value| - if key == "name" && value.start_with?("datadog-") - installed_list.push(value["datadog-".length..-1]) - end - end - end - else - raise "Failed to list pip installed packages" - end - end - - checks_to_install.each do |check| - check_dir = File.join(project_dir, check) - check_conf_dir = "#{conf_dir}/#{check}.d" - - # For each conf file, if it already exists, that means the `datadog-agent` software def - # wrote it first. In that case, since the agent's confs take precedence, skip the conf - conf_files = ["conf.yaml.example", "conf.yaml.default", "metrics.yaml", "auto_conf.yaml"] - conf_files.each do |filename| - src = windows_safe_path(check_dir,"datadog_checks", check, "data", filename) - dest = check_conf_dir - if File.exist?(src) and !File.exist?(windows_safe_path(dest, filename)) - FileUtils.mkdir_p(dest) - FileUtils.cp_r(src, dest) - end - end - - # Copy SNMP profiles - profile_folders = ['profiles', 'default_profiles'] - profile_folders.each do |profile_folder| - folder_path = "#{check_dir}/datadog_checks/#{check}/data/#{profile_folder}" - if File.exist? folder_path - FileUtils.cp_r folder_path, "#{check_conf_dir}/" - end - end - - # pip < 21.2 replace underscores by dashes in package names per https://pip.pypa.io/en/stable/news/#v21-2 - # whether or not this might switch back in the future is not guaranteed, so we check for both name - # with dashes and underscores - if installed_list.include?(check) || installed_list.include?(check.gsub('_', '-')) - next - end - - if windows_target? - shellout! "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => win_build_env, :cwd => "#{windows_safe_path(project_dir)}\\#{check}" - else - shellout! "#{python} -m pip wheel . --no-deps --no-index --wheel-dir=#{wheel_build_dir}", :env => nix_build_env, :cwd => "#{project_dir}/#{check}" - end - shellout! "#{python} -m pip install datadog-#{check} --no-deps --no-index --find-links=#{wheel_build_dir}" - if cache_bucket != '' && ENV.fetch('INTEGRATION_WHEELS_SKIP_CACHE_UPLOAD', '') == '' && cache_branch != nil - shellout! "inv -e agent.upload-integration-to-cache " \ - "--python #{python_major_version} --bucket #{cache_bucket} " \ - "--branch #{cache_branch} " \ - "--integrations-dir #{windows_safe_path(project_dir)} " \ - "--build-dir #{wheel_build_dir} " \ - "--integration #{check} " \ - "--awscli #{awscli}", - :cwd => tasks_dir_in - end - end - end - - # From now on we don't need piptools anymore, uninstall its deps so we don't include them in the final artifact - uninstall_buildtime_deps.each do |dep| - command "#{python} -m pip uninstall -y #{dep}" - end - - if windows_target? - site_packages = windows_safe_path(install_dir, "embedded#{python_major_version}", "Lib/site-packages") - else - if python_major_version == '2' - site_packages = File.join(install_dir, "embedded/lib/python2.7/site-packages") - else - site_packages = File.join(install_dir, "embedded/lib/python3.11/site-packages") - end - end - - # Patch applies to only one file: set it explicitly as a target, no need for -p - patch :source => "remove-maxfile-maxpath-psutil.patch", :target => windows_safe_path(site_packages, "psutil/__init__.py") - - if python_major_version == '2' - patch :source => "create-regex-at-runtime.patch", :target => windows_safe_path(site_packages, "yaml/reader.py") - end - - # Run pip check to make sure the agent's python environment is clean, all the dependencies are compatible - command "#{python} -m pip check" - - # Removing tests that don't need to be shipped in the embedded folder - delete windows_safe_path(site_packages, "Cryptodome/SelfTest") - - # Ship `requirements-agent-release.txt` file containing the versions of every check shipped with the agent - # Used by the `datadog-agent integration` command to prevent downgrading a check to a version - # older than the one shipped in the agent - copy "#{project_dir}/requirements-agent-release.txt", "#{install_dir}/" - end -end diff --git a/omnibus/config/templates/datadog-agent-integrations/static_requirements.txt.erb b/omnibus/config/templates/datadog-agent-integrations-py2/static_requirements.txt.erb similarity index 100% rename from omnibus/config/templates/datadog-agent-integrations/static_requirements.txt.erb rename to omnibus/config/templates/datadog-agent-integrations-py2/static_requirements.txt.erb diff --git a/omnibus/config/templates/datadog-agent-integrations-py3/static_requirements.txt.erb b/omnibus/config/templates/datadog-agent-integrations-py3/static_requirements.txt.erb new file mode 100644 index 0000000000000..cf827abae0167 --- /dev/null +++ b/omnibus/config/templates/datadog-agent-integrations-py3/static_requirements.txt.erb @@ -0,0 +1,3 @@ +<% requirements.each do |requirement| -%> +<%= requirement -%> +<% end -%> From 296a5201f6fbeb027ea0ff3af1764553964e18c6 Mon Sep 17 00:00:00 2001 From: Pedro Lambert Date: Wed, 29 Nov 2023 14:22:35 -0500 Subject: [PATCH 80/87] [USMON-623] Mitigate races causing Go TLS test failures (#20829) * [wip] Fix race condition when server/client are on the same host * Address `http_in_flight` race * Disable keep-alives * Add documentation for `mark_protocol_direction` * Update `is_uprobe_context` --- .../ebpf/c/protocols/classification/defs.h | 2 + .../classification/shared-tracer-maps.h | 10 +++++ pkg/network/ebpf/c/protocols/http/http.h | 45 ++++++++++++++++--- pkg/network/ebpf/c/tracer/stats.h | 34 ++++++++++++++ pkg/network/tracer/tracer_usm_linux_test.go | 4 +- 5 files changed, 88 insertions(+), 7 deletions(-) diff --git a/pkg/network/ebpf/c/protocols/classification/defs.h b/pkg/network/ebpf/c/protocols/classification/defs.h index a93942a2f1432..8f56a739783d1 100644 --- a/pkg/network/ebpf/c/protocols/classification/defs.h +++ b/pkg/network/ebpf/c/protocols/classification/defs.h @@ -34,6 +34,8 @@ #define FLAG_NPM_ENABLED 1 << 2 #define FLAG_TCP_CLOSE_DELETION 1 << 3 #define FLAG_SOCKET_FILTER_DELETION 1 << 4 +#define FLAG_SERVER_SIDE 1 << 5 +#define FLAG_CLIENT_SIDE 1 << 6 // The enum below represents all different protocols we're able to // classify. Entries are segmented such that it is possible to infer the diff --git a/pkg/network/ebpf/c/protocols/classification/shared-tracer-maps.h b/pkg/network/ebpf/c/protocols/classification/shared-tracer-maps.h index 1cf95e67afb9a..cb408612dcf84 100644 --- a/pkg/network/ebpf/c/protocols/classification/shared-tracer-maps.h +++ b/pkg/network/ebpf/c/protocols/classification/shared-tracer-maps.h @@ -102,6 +102,16 @@ __maybe_unused static __always_inline void delete_protocol_stack(conn_tuple_t* n return; } deletion: + if (stack->flags&FLAG_SERVER_SIDE && stack->flags&FLAG_CLIENT_SIDE) { + // If we reach this code path it means both client and server are + // present in this host. To avoid a race condition where one side + // potentially deletes protocol information before the other gets a + // chance to retrieve it, we clear these flags and bail out, which + // defers the deletion of protocol data to the last one to reach this + // code path. + stack->flags = stack->flags & ~(FLAG_SERVER_SIDE|FLAG_CLIENT_SIDE); + return; + } bpf_map_delete_elem(&connection_protocol, normalized_tuple); } diff --git a/pkg/network/ebpf/c/protocols/http/http.h b/pkg/network/ebpf/c/protocols/http/http.h index 8ee1d4797a988..b3ca4ca9d9972 100644 --- a/pkg/network/ebpf/c/protocols/http/http.h +++ b/pkg/network/ebpf/c/protocols/http/http.h @@ -78,13 +78,45 @@ static __always_inline bool http_closed(skb_info_t *skb_info) { return (skb_info && skb_info->tcp_flags&(TCPHDR_FIN|TCPHDR_RST)); } +// this is merely added here to improve readibility of code. +// HTTP monitoring code is executed in two "contexts": +// * via a socket filter program, which is used for monitoring plain traffic; +// * via a uprobe-based programs, for the purposes of tracing encrypted traffic (SSL, Go TLS, Java TLS etc); +// When code is executed from uprobes, skb_info is null[1]. +// +// [1] There is one notable exception that happens when we process uprobes +// triggering the termination of connections. In that particular context we +// "inject" a special skb_info that has the tcp_flags field set to `TCPHDR_FIN`. +static __always_inline bool is_uprobe_context(skb_info_t *skb_info) { + return skb_info == NULL || (skb_info->data_end == 0 && http_closed(skb_info)); +} + // The purpose of http_seen_before is to is to avoid re-processing certain TCP segments. // We only care about 3 types of segments: // * A segment with the beginning of a request (packet_type == HTTP_REQUEST); // * A segment with the beginning of a response (packet_type == HTTP_RESPONSE); // * A segment with a (FIN|RST) flag set; static __always_inline bool http_seen_before(http_transaction_t *http, skb_info_t *skb_info, http_packet_t packet_type) { - if (!skb_info) { + if (is_uprobe_context(skb_info) && !http_closed(skb_info)) { + // The purpose of setting tcp_seq = 0 in the context of uprobe tracing + // is innocuous for the most part (as this field will almost aways be 0) + // The only reason we do this here is to *minimize* the chance of a race + // condition that happens sometimes in the context of uprobe-based tracing: + // + // 1) handle_request for c1 (uprobe) + // 2) socket filter triggers termination code for c1 (server -> FIN -> client) + // 3) handle_response for c1 (uprobe) + // 4) socket filter triggers termination code for c1 (client -> FIN -> server) + // + // The problem is that 2) and 3) might happen in parallel, and 2) may + // delete the the eBPF data *before* 4) executes and flushes the data + // with both request and response information to userspace. + // + // Since we check if (skb_info->tcp_seq == HTTP_TERMINATING) evaluates + // to true before flushing and deleting the eBPF map data, setting it to + // 0 here gives a chance for the late response to "cancel" the map + // deletion. + http->tcp_seq = 0; return false; } @@ -170,12 +202,15 @@ static __always_inline void http_process(http_event_t *event, skb_info_t *skb_in http->response_last_seen = bpf_ktime_get_ns(); } - if (http_closed(skb_info)) { + if (http->tcp_seq == HTTP_TERMINATING) { http_batch_enqueue_wrapper(tuple, http); - bpf_map_delete_elem(&http_in_flight, tuple); + // Check a second time to minimize the chance of accidentally deleting a + // map entry if there is a race with a late response. + // Please refer to comments in `http_seen_before` for more context. + if (http->tcp_seq == HTTP_TERMINATING) { + bpf_map_delete_elem(&http_in_flight, tuple); + } } - - return; } // this function is called by the socket-filter program to decide whether or not we should inspect diff --git a/pkg/network/ebpf/c/tracer/stats.h b/pkg/network/ebpf/c/tracer/stats.h index 3fb663daefd23..07c9948527004 100644 --- a/pkg/network/ebpf/c/tracer/stats.h +++ b/pkg/network/ebpf/c/tracer/stats.h @@ -57,6 +57,38 @@ static __always_inline void update_conn_state(conn_tuple_t *t, conn_stats_ts_t * } } +// this function marks the protocol stack object with the connection direction +// +// *how is the connection direction determined?* +// +// Basically we compare the src-side of the normalized USM tuple (which should +// contain the client port), with the source port of the TCP *socket* (here +// supplied as part the `pre_norm_tuple` argument). If they match, we mark the +// protocol stack with FLAG_CLIENT_SIDE, otherwise we mark it with +// FLAG_SERVER_SIDE. +// +// *why do we do that?* +// +// We do this to mitigate a race condition that may arise in the context of +// localhost traffic when deleting the protocol_stack_t entry. This means that +// we're pretty much only interested in the case where a protocol stack is +// annothed with *both* FLAG_SERVER_SIDE and FLAG_CLIENT_SIDE. For more context +// refer to classification/shared-tracer-maps.h +// +// *what if there is something wrong with the USM normalization?* +// +// This doesn't matter in our case. Even if FLAG_SERVER_SIDE and +// FLAG_CLIENT_SIDE are flipped, all we care about is the case where both flags +// are present. +static __always_inline void mark_protocol_direction(conn_tuple_t *pre_norm_tuple, conn_tuple_t *norm_tuple, protocol_stack_t *protocol_stack) { + if (pre_norm_tuple->sport == norm_tuple->sport) { + set_protocol_flag(protocol_stack, FLAG_CLIENT_SIDE); + return; + } + + set_protocol_flag(protocol_stack, FLAG_SERVER_SIDE); +} + static __always_inline void update_protocol_classification_information(conn_tuple_t *t, conn_stats_ts_t *stats) { if (is_fully_classified(&stats->protocol_stack)) { return; @@ -71,6 +103,7 @@ static __always_inline void update_protocol_classification_information(conn_tupl protocol_stack_t *protocol_stack = __get_protocol_stack(&conn_tuple_copy); set_protocol_flag(protocol_stack, FLAG_NPM_ENABLED); + mark_protocol_direction(t, &conn_tuple_copy, protocol_stack); merge_protocol_stacks(&stats->protocol_stack, protocol_stack); conn_tuple_t *cached_skb_conn_tup_ptr = bpf_map_lookup_elem(&conn_tuple_to_socket_skb_conn_tuple, &conn_tuple_copy); @@ -81,6 +114,7 @@ static __always_inline void update_protocol_classification_information(conn_tupl conn_tuple_copy = *cached_skb_conn_tup_ptr; protocol_stack = __get_protocol_stack(&conn_tuple_copy); set_protocol_flag(protocol_stack, FLAG_NPM_ENABLED); + mark_protocol_direction(t, &conn_tuple_copy, protocol_stack); merge_protocol_stacks(&stats->protocol_stack, protocol_stack); } diff --git a/pkg/network/tracer/tracer_usm_linux_test.go b/pkg/network/tracer/tracer_usm_linux_test.go index 5807ef53961de..2b51d79a89cad 100644 --- a/pkg/network/tracer/tracer_usm_linux_test.go +++ b/pkg/network/tracer/tracer_usm_linux_test.go @@ -832,7 +832,7 @@ func testHTTPGoTLSCaptureNewProcess(t *testing.T, cfg *config.Config) { // Setup closeServer := testutil.HTTPServer(t, serverAddr, testutil.Options{ EnableTLS: true, - EnableKeepAlive: true, + EnableKeepAlive: false, }) t.Cleanup(closeServer) @@ -873,7 +873,7 @@ func testHTTPGoTLSCaptureAlreadyRunning(t *testing.T, cfg *config.Config) { // Setup closeServer := testutil.HTTPServer(t, serverAddr, testutil.Options{ EnableTLS: true, - EnableKeepAlive: true, + EnableKeepAlive: false, }) t.Cleanup(closeServer) From 1b4030fd4e3b08b49bafa56a28f9dd67bc75dc7c Mon Sep 17 00:00:00 2001 From: Bryce Kahle Date: Wed, 29 Nov 2023 11:53:28 -0800 Subject: [PATCH 81/87] Add CentOS 8 to KMT (#21187) * add centos 8 * update 7.9 image locations * use master images --- .gitlab/kernel_version_testing/system_probe.yml | 4 ++-- test/new-e2e/system-probe/config/vmconfig-arm64.json | 9 +++++++-- test/new-e2e/system-probe/config/vmconfig-x64.json | 9 +++++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.gitlab/kernel_version_testing/system_probe.yml b/.gitlab/kernel_version_testing/system_probe.yml index 1fc2385e2f4c3..b5005039c0f12 100644 --- a/.gitlab/kernel_version_testing/system_probe.yml +++ b/.gitlab/kernel_version_testing/system_probe.yml @@ -401,7 +401,7 @@ kernel_matrix_testing_run_tests_x64: ARCH: "x86_64" parallel: matrix: - - TAG: ["ubuntu_16.04", "ubuntu_18.04", "ubuntu_20.04", "ubuntu_22.04", "ubuntu_23.10", "amzn_4.14", "amzn_5.4", "amzn_5.10", "fedora_37", "fedora_38", "debian_10", "debian_11", "debian_12", "centos_79"] + - TAG: ["ubuntu_16.04", "ubuntu_18.04", "ubuntu_20.04", "ubuntu_22.04", "ubuntu_23.10", "amzn_4.14", "amzn_5.4", "amzn_5.10", "fedora_37", "fedora_38", "debian_10", "debian_11", "debian_12", "centos_79", "centos_8"] kernel_matrix_testing_run_tests_arm64: extends: @@ -414,7 +414,7 @@ kernel_matrix_testing_run_tests_arm64: ARCH: "arm64" parallel: matrix: - - TAG: ["ubuntu_18.04", "ubuntu_20.04", "ubuntu_22.04", "ubuntu_23.10", "amzn_4.14", "amzn_5.4", "amzn_5.10", "fedora_37", "fedora_38", "debian_10", "debian_11", "debian_12", "centos_79"] + - TAG: ["ubuntu_18.04", "ubuntu_20.04", "ubuntu_22.04", "ubuntu_23.10", "amzn_4.14", "amzn_5.4", "amzn_5.10", "fedora_37", "fedora_38", "debian_10", "debian_11", "debian_12", "centos_79", "centos_8"] .kernel_matrix_testing_cleanup: stage: kernel_matrix_testing diff --git a/test/new-e2e/system-probe/config/vmconfig-arm64.json b/test/new-e2e/system-probe/config/vmconfig-arm64.json index 1565b3d59f9ef..045a20eda7380 100644 --- a/test/new-e2e/system-probe/config/vmconfig-arm64.json +++ b/test/new-e2e/system-probe/config/vmconfig-arm64.json @@ -67,9 +67,14 @@ "image_source": "https://dd-agent-omnibus.s3.amazonaws.com/kernel-version-testing/rootfs/master/debian-12-generic-arm64.qcow2.xz" }, { - "dir": "CentOS-7-aarch64-GenericCloud-2211.qcow2", + "dir": "centos-7.9-arm64.qcow2", "tag": "centos_79", - "image_source": "https://dd-agent-omnibus.s3.amazonaws.com/kernel-version-testing/rootfs/master/CentOS-7-aarch64-GenericCloud-2211.qcow2.xz" + "image_source": "https://dd-agent-omnibus.s3.amazonaws.com/kernel-version-testing/rootfs/master/centos-7.9-arm64.qcow2.xz" + }, + { + "dir": "centos-8-arm64.qcow2", + "tag": "centos_8", + "image_source": "https://dd-agent-omnibus.s3.amazonaws.com/kernel-version-testing/rootfs/master/centos-8-arm64.qcow2.xz" } ], "machine": "virt", diff --git a/test/new-e2e/system-probe/config/vmconfig-x64.json b/test/new-e2e/system-probe/config/vmconfig-x64.json index 510ce5665b08d..39ac353d57072 100644 --- a/test/new-e2e/system-probe/config/vmconfig-x64.json +++ b/test/new-e2e/system-probe/config/vmconfig-x64.json @@ -72,9 +72,14 @@ "image_source": "https://dd-agent-omnibus.s3.amazonaws.com/kernel-version-testing/rootfs/master/debian-12-generic-amd64.qcow2.xz" }, { - "dir": "CentOS-7-x86_64-GenericCloud-2211.qcow2", + "dir": "centos-7.9-x86_64.qcow2", "tag": "centos_79", - "image_source": "https://dd-agent-omnibus.s3.amazonaws.com/kernel-version-testing/rootfs/master/CentOS-7-x86_64-GenericCloud-2211.qcow2.xz" + "image_source": "https://dd-agent-omnibus.s3.amazonaws.com/kernel-version-testing/rootfs/master/centos-7.9-x86_64.qcow2.xz" + }, + { + "dir": "centos-8-x86_64.qcow2", + "tag": "centos_8", + "image_source": "https://dd-agent-omnibus.s3.amazonaws.com/kernel-version-testing/rootfs/master/centos-8-x86_64.qcow2.xz" } ], "vcpu": [ From 1ceefdded7b8570f5f7f408d21f50d6cb969183f Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Wed, 29 Nov 2023 17:12:08 -0500 Subject: [PATCH 82/87] Updates unit in telemetry description to match reporting code (#21179) --- comp/dogstatsd/server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/comp/dogstatsd/server/server.go b/comp/dogstatsd/server/server.go index 65d10ccac4744..8702b96b44928 100644 --- a/comp/dogstatsd/server/server.go +++ b/comp/dogstatsd/server/server.go @@ -179,7 +179,7 @@ func initTelemetry(cfg config.Reader, logger logComponent.Component) { "dogstatsd", "channel_latency", []string{"shard", "message_type"}, - "Time in millisecond to push metrics to the aggregator input buffer", + "Time in nanosecond to push metrics to the aggregator input buffer", buckets) listeners.InitTelemetry(get("telemetry.dogstatsd.listeners_latency_buckets")) From d72baa9ef39ef4176d4aedab02eb8c19a5515b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Beauz=C3=A9e-Luyssen?= Date: Thu, 30 Nov 2023 09:39:53 +0100 Subject: [PATCH 83/87] tasks: release: group cleanup tasks in a single release.cleanup command (#21142) tasks: release: group cleanup tasks in a single release.cleanup command --- tasks/release.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tasks/release.py b/tasks/release.py index 324fc28f28341..e8fd6a2663f26 100644 --- a/tasks/release.py +++ b/tasks/release.py @@ -18,7 +18,7 @@ from .libs.common.user_interactions import yes_no_question from .libs.version import Version from .modules import DEFAULT_MODULES -from .pipeline import run +from .pipeline import edit_schedule, run from .utils import ( DEFAULT_BRANCH, GITHUB_REPO_NAME, @@ -1358,18 +1358,10 @@ def unfreeze(ctx, base_directory="~/dd", major_versions="6,7", upstream="origin" create_and_update_release_branch(ctx, repo, release_branch, base_directory=base_directory, upstream=upstream) -@task -def update_last_stable(_, major_versions="6,7"): +def _update_last_stable(_, version, major_versions="6,7"): """ Updates the last_release field(s) of release.json """ - gh = GithubAPI('datadog/datadog-agent') - latest_release = gh.latest_release() - match = VERSION_RE.search(latest_release) - if not match: - raise Exit(f'Unexpected version fetched from github {latest_release}', code=1) - version = _create_version_from_match(match) - release_json = _load_release_json() list_major_versions = parse_major_versions(major_versions) # If the release isn't a RC, update the last stable release field @@ -1379,6 +1371,24 @@ def update_last_stable(_, major_versions="6,7"): _save_release_json(release_json) +@task +def cleanup(ctx): + """ + Perform the post release cleanup steps + Currently this: + - Updates the scheduled nightly pipeline to target the new stable branch + - Updates the release.json last_stable fields + """ + gh = GithubAPI('datadog/datadog-agent') + latest_release = gh.latest_release() + match = VERSION_RE.search(latest_release) + if not match: + raise Exit(f'Unexpected version fetched from github {latest_release}', code=1) + version = _create_version_from_match(match) + _update_last_stable(ctx, version) + edit_schedule(ctx, 2555, ref=version.branch()) + + @task def check_omnibus_branches(_): for branch in ['nightly', 'nightly-a7']: From e94970c800daf9739b42d224ebda4826263fc123 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 09:56:25 +0100 Subject: [PATCH 84/87] CWS: sync BTFhub constants (#21192) Co-authored-by: paulcacheux --- pkg/security/probe/constantfetch/btfhub/constants.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/security/probe/constantfetch/btfhub/constants.json b/pkg/security/probe/constantfetch/btfhub/constants.json index 18187d76dd199..4f33d57a1a90d 100644 --- a/pkg/security/probe/constantfetch/btfhub/constants.json +++ b/pkg/security/probe/constantfetch/btfhub/constants.json @@ -1,5 +1,5 @@ { - "commit": "ab2b4260f230140be6c99dc5e538e224d8b2dd95", + "commit": "b6b3f8186864ec31c48168c1cc1bce70aac65da0", "constants": [ { "binprm_file_offset": 168, @@ -18445,6 +18445,13 @@ "uname_release": "4.14.35-2047.532.2.el7uek.x86_64", "cindex": 86 }, + { + "distrib": "ol", + "version": "7", + "arch": "x86_64", + "uname_release": "4.14.35-2047.532.3.el7uek.x86_64", + "cindex": 86 + }, { "distrib": "ol", "version": "7", From 0fa6ade7b00f2fcc48052f05f660d507bd140699 Mon Sep 17 00:00:00 2001 From: Kevin Fairise <132568982+KevinFairise2@users.noreply.github.com> Date: Thu, 30 Nov 2023 10:09:31 +0100 Subject: [PATCH 85/87] AP-2322 Move Amazon linux install script test to new-e2e (#21149) AP-2322 Move Amazon linux install script test to new-e2e --- .gitlab/e2e_test_junit_upload.yml | 4 + .gitlab/kitchen_testing.yml | 1 - .gitlab/kitchen_testing/amazonlinux.yml | 96 ------------------- .gitlab/kitchen_tests_upload.yml | 4 - .gitlab/new-e2e_testing.yml | 1 + .gitlab/new-e2e_testing/amazonlinux.yml | 90 +++++++++++++++++ .../agent-platform/common/pkg-manager/yum.go | 25 +++++ .../agent-platform/common/test_client.go | 4 + .../agent-platform/platforms/platforms.json | 14 +++ 9 files changed, 138 insertions(+), 101 deletions(-) delete mode 100644 .gitlab/kitchen_testing/amazonlinux.yml create mode 100644 .gitlab/new-e2e_testing/amazonlinux.yml create mode 100644 test/new-e2e/tests/agent-platform/common/pkg-manager/yum.go diff --git a/.gitlab/e2e_test_junit_upload.yml b/.gitlab/e2e_test_junit_upload.yml index 52a01639669f9..8ebbaa09302fb 100644 --- a/.gitlab/e2e_test_junit_upload.yml +++ b/.gitlab/e2e_test_junit_upload.yml @@ -19,6 +19,10 @@ e2e_test_junit_upload: - new-e2e-agent-platform-install-script-debian-iot-agent-a7-x86_64 - new-e2e-agent-platform-install-script-debian-dogstatsd-a7-x86_64 - new-e2e-agent-platform-install-script-debian-heroku-agent-a7-x86_64 + - new-e2e-agent-platform-install-script-amazonlinux-a6-x86_64 + - new-e2e-agent-platform-install-script-amazonlinux-a6-arm64 + - new-e2e-agent-platform-install-script-amazonlinux-a7-x64 + - new-e2e-agent-platform-install-script-amazonlinux-a7-arm64 - new-e2e-npm-main - new-e2e-cws-main script: diff --git a/.gitlab/kitchen_testing.yml b/.gitlab/kitchen_testing.yml index b55e87219cacd..207984e36ebd1 100644 --- a/.gitlab/kitchen_testing.yml +++ b/.gitlab/kitchen_testing.yml @@ -3,7 +3,6 @@ # Contains jobs which run kitchen tests on the Agent packages. include: - - /.gitlab/kitchen_testing/amazonlinux.yml - /.gitlab/kitchen_testing/centos.yml - /.gitlab/kitchen_testing/debian.yml - /.gitlab/kitchen_testing/suse.yml diff --git a/.gitlab/kitchen_testing/amazonlinux.yml b/.gitlab/kitchen_testing/amazonlinux.yml deleted file mode 100644 index 792d307858a9d..0000000000000 --- a/.gitlab/kitchen_testing/amazonlinux.yml +++ /dev/null @@ -1,96 +0,0 @@ ---- -# FIXME: our current Gitlab version doesn't support importing a file more than once -# For now, the workaround is to include "common" files once in the top-level .gitlab-ci.yml file -# See: https://gitlab.com/gitlab-org/gitlab/-/issues/28987 -# include: -# - /.gitlab/kitchen_common/testing.yml - - -# Kitchen: OSes -# ------------- - -.kitchen_os_amazonlinux: - variables: - KITCHEN_PLATFORM: "amazonlinux" - before_script: - - cd $DD_AGENT_TESTING_DIR - - tasks/kitchen_setup.sh - -# Kitchen: scenarios (os * agent * (cloud + arch)) -# ------------------------------- - -.kitchen_scenario_amazonlinux_a6_x64: - variables: - KITCHEN_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" - KITCHEN_CWS_SUPPORTED_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" - DEFAULT_KITCHEN_OSVERS: "amazonlinux2023" - extends: - - .kitchen_agent_a6 - - .kitchen_os_amazonlinux - - .kitchen_ec2_x64 - needs: ["deploy_rpm_testing-a6_x64"] - -.kitchen_scenario_amazonlinux_a7_x64: - variables: - KITCHEN_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" - KITCHEN_CWS_SUPPORTED_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" - DEFAULT_KITCHEN_OSVERS: "amazonlinux2023" - extends: - - .kitchen_agent_a7 - - .kitchen_os_amazonlinux - - .kitchen_ec2_x64 - needs: ["deploy_rpm_testing-a7_x64"] - -.kitchen_scenario_amazonlinux_a6_arm64: - variables: - KITCHEN_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" - KITCHEN_CWS_SUPPORTED_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" - DEFAULT_KITCHEN_OSVERS: "amazonlinux2023" - extends: - - .kitchen_agent_a6 - - .kitchen_os_amazonlinux - - .kitchen_ec2_arm64 - needs: ["deploy_rpm_testing-a6_arm64"] - -.kitchen_scenario_amazonlinux_a7_arm64: - variables: - KITCHEN_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" - KITCHEN_CWS_SUPPORTED_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" - DEFAULT_KITCHEN_OSVERS: "amazonlinux2023" - extends: - - .kitchen_agent_a7 - - .kitchen_os_amazonlinux - - .kitchen_ec2_arm64 - needs: ["deploy_rpm_testing-a7_arm64"] - - # Kitchen: final test matrix (tests * scenarios) -# ---------------------------------------------- - -kitchen_amazonlinux_install_script_agent-a6_x64: - extends: - - .kitchen_os_with_cws - - .kitchen_scenario_amazonlinux_a6_x64 - - .kitchen_test_install_script_agent - -kitchen_amazonlinux_install_script_agent-a6_arm64: - extends: - - .kitchen_os_with_cws - - .kitchen_scenario_amazonlinux_a6_arm64 - - .kitchen_test_install_script_agent - -kitchen_amazonlinux_install_script_agent-a7_x64: - # Run install script test on branches, on a reduced number of platforms - rules: - !reference [.on_default_kitchen_tests_a7] - extends: - - .kitchen_os_with_cws - - .kitchen_scenario_amazonlinux_a7_x64 - - .kitchen_test_install_script_agent - -kitchen_amazonlinux_install_script_agent-a7_arm64: - rules: - !reference [.on_all_kitchen_builds_a7] - extends: - - .kitchen_os_with_cws - - .kitchen_scenario_amazonlinux_a7_arm64 - - .kitchen_test_install_script_agent diff --git a/.gitlab/kitchen_tests_upload.yml b/.gitlab/kitchen_tests_upload.yml index ef2982f377c00..0c864030d3fcd 100644 --- a/.gitlab/kitchen_tests_upload.yml +++ b/.gitlab/kitchen_tests_upload.yml @@ -7,10 +7,6 @@ kitchen_tests_upload_common: - !reference [.except_mergequeue] - when: always dependencies: - - kitchen_amazonlinux_install_script_agent-a6_arm64 - - kitchen_amazonlinux_install_script_agent-a6_x64 - - kitchen_amazonlinux_install_script_agent-a7_arm64 - - kitchen_amazonlinux_install_script_agent-a7_x64 - kitchen_centos_fips_install_script_agent-a6 - kitchen_centos_fips_install_script_agent-a7 - kitchen_centos_fips_install_script_dogstatsd-a7 diff --git a/.gitlab/new-e2e_testing.yml b/.gitlab/new-e2e_testing.yml index 5123c566b4c7f..66350e39a3f31 100644 --- a/.gitlab/new-e2e_testing.yml +++ b/.gitlab/new-e2e_testing.yml @@ -4,3 +4,4 @@ include: - /.gitlab/new-e2e_testing/debian.yml + - /.gitlab/new-e2e_testing/amazonlinux.yml diff --git a/.gitlab/new-e2e_testing/amazonlinux.yml b/.gitlab/new-e2e_testing/amazonlinux.yml new file mode 100644 index 0000000000000..55ae3ce302f0b --- /dev/null +++ b/.gitlab/new-e2e_testing/amazonlinux.yml @@ -0,0 +1,90 @@ + +.new-e2e_os_amazonlinux: + variables: + E2E_PLATFORM: amazonlinux + +.new-e2e_install_script: + variables: + TARGETS: ./tests/agent-platform/install-script + TEAM: agent-platform + EXTRA_PARAMS: --osversion $E2E_OSVERS --platform $E2E_PLATFORM --cws-supported-osversion $E2E_CWS_SUPPORTED_OSVERS --major-version $AGENT_MAJOR_VERSION --arch $E2E_ARCH --flavor $FLAVOR + +.new-e2e_amazonlinux_a6_x86_64: + variables: + E2E_ARCH: x86_64 + E2E_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" + E2E_CWS_SUPPORTED_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" + E2E_BRANCH_OSVERS: "amazonlinux2023" + needs: ["deploy_rpm_testing-a6_x64"] + +.new-e2e_amazonlinux_a6_arm64: + variables: + E2E_ARCH: arm64 + E2E_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" + E2E_CWS_SUPPORTED_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" + E2E_BRANCH_OSVERS: "amazonlinux2023" + needs: ["deploy_rpm_testing-a6_arm64"] + +.new-e2e_amazonlinux_a7_x86_64: + variables: + E2E_ARCH: x86_64 + E2E_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" + E2E_CWS_SUPPORTED_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" + E2E_BRANCH_OSVERS: "amazonlinux2023" + needs: ["deploy_rpm_testing-a7_x64"] + +.new-e2e_amazonlinux_a7_arm64: + variables: + E2E_ARCH: arm64 + E2E_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" + E2E_CWS_SUPPORTED_OSVERS: "amazonlinux2-5-10,amazonlinux2022-5-15,amazonlinux2023" + E2E_BRANCH_OSVERS: "amazonlinux2023" + needs: ["deploy_rpm_testing-a7_arm64"] + +new-e2e-agent-platform-install-script-amazonlinux-a6-x86_64: + stage: kitchen_testing + extends: + - .new_e2e_template + - .new-e2e_install_script + - .new-e2e_os_amazonlinux + - .new-e2e_amazonlinux_a6_x86_64 + - .new-e2e_agent_a6 + variables: + FLAVOR: datadog-agent + +new-e2e-agent-platform-install-script-amazonlinux-a6-arm64: + stage: kitchen_testing + extends: + - .new_e2e_template + - .new-e2e_install_script + - .new-e2e_os_amazonlinux + - .new-e2e_amazonlinux_a6_arm64 + - .new-e2e_agent_a6 + variables: + FLAVOR: datadog-agent + +new-e2e-agent-platform-install-script-amazonlinux-a7-x64: + stage: kitchen_testing + extends: + - .new_e2e_template + - .new-e2e_install_script + - .new-e2e_os_amazonlinux + - .new-e2e_amazonlinux_a7_x86_64 + - .new-e2e_agent_a7 + rules: + !reference [.on_default_new-e2e_tests_a7] + variables: + FLAVOR: datadog-agent + +new-e2e-agent-platform-install-script-amazonlinux-a7-arm64: + stage: kitchen_testing + extends: + - .new_e2e_template + - .new-e2e_install_script + - .new-e2e_os_amazonlinux + - .new-e2e_amazonlinux_a7_arm64 + - .new-e2e_agent_a7 + rules: + !reference [.on_all_new-e2e_tests_a7] + variables: + FLAVOR: datadog-agent diff --git a/test/new-e2e/tests/agent-platform/common/pkg-manager/yum.go b/test/new-e2e/tests/agent-platform/common/pkg-manager/yum.go new file mode 100644 index 0000000000000..59712a1156ba4 --- /dev/null +++ b/test/new-e2e/tests/agent-platform/common/pkg-manager/yum.go @@ -0,0 +1,25 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package pkgmanager + +import ( + e2eClient "github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e/client" +) + +// YumPackageManager struct for the Yum package manager +type YumPackageManager struct { + vmClient e2eClient.VM +} + +// NewYumPackageManager return yum package manager +func NewYumPackageManager(vmClient e2eClient.VM) *YumPackageManager { + return &YumPackageManager{vmClient: vmClient} +} + +// Remove executes remove command from yum +func (s *YumPackageManager) Remove(pkg string) (string, error) { + return s.vmClient.ExecuteWithError("sudo yum remove -y " + pkg) +} diff --git a/test/new-e2e/tests/agent-platform/common/test_client.go b/test/new-e2e/tests/agent-platform/common/test_client.go index e842e73f2f54f..18505d7e1da43 100644 --- a/test/new-e2e/tests/agent-platform/common/test_client.go +++ b/test/new-e2e/tests/agent-platform/common/test_client.go @@ -58,6 +58,10 @@ func getPackageManager(vmClient e2eClient.VM) PackageManager { if _, err := vmClient.ExecuteWithError("command -v apt"); err == nil { return pkgmanager.NewAptPackageManager(vmClient) } + + if _, err := vmClient.ExecuteWithError("command -v yum"); err == nil { + return pkgmanager.NewYumPackageManager(vmClient) + } return nil } diff --git a/test/new-e2e/tests/agent-platform/platforms/platforms.json b/test/new-e2e/tests/agent-platform/platforms/platforms.json index 7f905398eef76..4f086ca81f654 100644 --- a/test/new-e2e/tests/agent-platform/platforms/platforms.json +++ b/test/new-e2e/tests/agent-platform/platforms/platforms.json @@ -11,5 +11,19 @@ "debian-11": "ami-03ea090ddd75eb738", "debian-12": "ami-02aab8d5301cb8d68" } + }, + "amazonlinux": { + "x86_64": { + "amazonlinux2-4-14": "ami-038b3df3312ddf25d", + "amazonlinux2-5-10": "ami-06a0cd9728546d178", + "amazonlinux2022-5-15": "ami-0309aede310b9cc1f", + "amazonlinux2023": "ami-0889a44b331db0194" + }, + "arm64": { + "amazonlinux2-4-14": "ami-090230ed0c6b13c74", + "amazonlinux2-5-10": "ami-09e51988f56677f44", + "amazonlinux2022-5-15": "ami-0a8495f6303122235", + "amazonlinux2023": "ami-08fc6fb8ad2e794bb" + } } } From 562e27557dea06601ab3c26043f23ea05a833f43 Mon Sep 17 00:00:00 2001 From: Gustavo Caso Date: Thu, 30 Nov 2023 10:38:13 +0100 Subject: [PATCH 86/87] [ASCII-950] Add test for `pkg/status/render` (#21106) Add unit test to validate status text output is correct --- .../subcommands/status/status.go | 5 +- .../subcommands/status/status_test.go | 8 +- pkg/status/render/fixtures/agent_status.text | 515 ++++++++++ pkg/status/render/fixtures/check_stats.json | 539 +++++++++++ pkg/status/render/fixtures/check_stats.text | 115 +++ .../render/fixtures/cluster_agent_status.json | 730 ++++++++++++++ .../render/fixtures/cluster_agent_status.text | 123 +++ .../render/fixtures/process_agent_status.json | 129 +++ .../render/fixtures/process_agent_status.text | 52 + .../fixtures/security_agent_status.json | 913 ++++++++++++++++++ .../fixtures/security_agent_status.text | 79 ++ pkg/status/render/render_test.go | 81 +- .../render/templates/autodiscovery.tmpl | 2 +- 13 files changed, 3274 insertions(+), 17 deletions(-) create mode 100755 pkg/status/render/fixtures/agent_status.text create mode 100644 pkg/status/render/fixtures/check_stats.json create mode 100755 pkg/status/render/fixtures/check_stats.text create mode 100644 pkg/status/render/fixtures/cluster_agent_status.json create mode 100755 pkg/status/render/fixtures/cluster_agent_status.text create mode 100644 pkg/status/render/fixtures/process_agent_status.json create mode 100755 pkg/status/render/fixtures/process_agent_status.text create mode 100644 pkg/status/render/fixtures/security_agent_status.json create mode 100755 pkg/status/render/fixtures/security_agent_status.text diff --git a/cmd/process-agent/subcommands/status/status.go b/cmd/process-agent/subcommands/status/status.go index c0e771c44aba3..1def211776ccf 100644 --- a/cmd/process-agent/subcommands/status/status.go +++ b/cmd/process-agent/subcommands/status/status.go @@ -139,7 +139,10 @@ func getAndWriteStatus(log log.Component, statusURL string, w io.Writer, options option(&s) } - body, err = json.Marshal(s) + status := map[string]interface{}{} + status["processAgentStatus"] = s + + body, err = json.Marshal(status) if err != nil { writeError(log, w, err) return diff --git a/cmd/process-agent/subcommands/status/status_test.go b/cmd/process-agent/subcommands/status/status_test.go index 6278cb086748e..f5d512fd1577d 100644 --- a/cmd/process-agent/subcommands/status/status_test.go +++ b/cmd/process-agent/subcommands/status/status_test.go @@ -42,7 +42,8 @@ func fakeStatusServer(t *testing.T, stats status.Status) *httptest.Server { func TestStatus(t *testing.T) { testTime := time.Now() - expectedStatus := status.Status{ + statusData := map[string]status.Status{} + statusInfo := status.Status{ Date: float64(testTime.UnixNano()), Core: status.CoreStatus{ Metadata: hostMetadataUtils.Payload{ @@ -51,12 +52,13 @@ func TestStatus(t *testing.T) { }, Expvars: status.ProcessExpvars{}, } + statusData["processAgentStatus"] = statusInfo - server := fakeStatusServer(t, expectedStatus) + server := fakeStatusServer(t, statusInfo) defer server.Close() // Build what the expected status should be - j, err := json.Marshal(expectedStatus) + j, err := json.Marshal(statusData) require.NoError(t, err) expectedOutput, err := render.FormatProcessAgentStatus(j) require.NoError(t, err) diff --git a/pkg/status/render/fixtures/agent_status.text b/pkg/status/render/fixtures/agent_status.text new file mode 100755 index 0000000000000..ba1b6c3e5b044 --- /dev/null +++ b/pkg/status/render/fixtures/agent_status.text @@ -0,0 +1,515 @@ + +============== +Agent (vx.y.z) +============== + + Status date: 2022-12-20 22:52:01.796 UTC (1671576721796) + Agent start: 2022-12-20 19:21:27.793 UTC (1671564087793) + Pid: 12136 + Go Version: go1.18.8 + Python Version: 3.8.14 + Build arch: amd64 + Agent flavor: agent + Check Runners: 4 + Log Level: INFO + + Paths + ===== + Config File: /etc/datadog-agent/datadog.yaml + conf.d: /etc/datadog-agent/conf.d + checks.d: /etc/datadog-agent/checks.d + + Clocks + ====== + NTP offset: 35µs + System time: 2022-12-20 22:52:01.796 UTC (1671576721796) + + Host Info + ========= + bootTime: 2022-12-20 19:19:03 UTC (1671563943000) + hostId: d23fb05c-2393-9a7a-fbf3-92cd755df12a + kernelArch: x86_64 + kernelVersion: 5.10.133+ + os: linux + platform: cos + platformVersion: 97 + procs: 211 + uptime: 2m37s + virtualizationRole: guest + + Hostnames + ========= + cluster-name: dd-sandbox + host_aliases: [gke-dd-sandbox-bits-8943422b-5wpg-dd-sandbox gke-dd-sandbox-bits-8943422b-5wpg.c.datadog-sandbox.internal gke-dd-sandbox-bits-8943422b-5wpg.datadog-sandbox] + hostname: gke-dd-sandbox-bits-8943422b-5wpg.c.datadog-sandbox.internal + socket-fqdn: dd-datadog-c4kcx + socket-hostname: dd-datadog-c4kcx + host tags: + cluster_name:dd-sandbox + kube_cluster_name:dd-sandbox + zone:asia-northeast1-a + internal-hostname:gke-dd-sandbox-bits-8943422b-5wpg.c.datadog-sandbox.internal + instance-id:90825865558996083 + project:datadog-sandbox + numeric_project_id:958371799887 + cluster-name:dd-sandbox + cluster-uid:3d6b7737edf6489fb1927577e24e8b0e314e6826aa3e47fa9b2eae419f261013 + cluster-location:asia-northeast1 + hostname provider: gce + unused hostname providers: + 'hostname' configuration/environment: hostname is empty + 'hostname_file' configuration/environment: 'hostname_file' configuration is not enabled + fargate: agent is not runnning on Fargate + + Metadata + ======== + agent_version: x.y.z + cloud_provider: GCP + config_apm_dd_url: + config_dd_url: + config_logs_dd_url: + config_logs_socks5_proxy_address: + config_no_proxy: [] + config_process_dd_url: + config_proxy_http: + config_proxy_https: + config_site: + feature_apm_enabled: false + feature_cspm_enabled: false + feature_cws_enabled: false + feature_logs_enabled: true + feature_networks_enabled: false + feature_networks_http_enabled: false + feature_networks_https_enabled: false + feature_otlp_enabled: false + feature_process_enabled: false + feature_processes_container_enabled: true + flavor: agent + hostname_source: gce + install_method_installer_version: datadog-3.6.4 + install_method_tool: helm + install_method_tool_version: Helm + logs_transport: TCP + +========= +Collector +========= + + Running Checks + ============== + + cilium (2.3.0) + -------------- + Instance ID: cilium:bac99095d52d45c [ERROR] + Configuration Source: file:/etc/datadog-agent/conf.d/cilium.d/auto_conf.yaml + Total Runs: 842 + Metric Samples: Last Run: 0, Total: 0 + Events: Last Run: 0, Total: 0 + Service Checks: Last Run: 1, Total: 842 + Average Execution Time : 8ms + Last Execution Date : 2022-12-20 22:51:54 UTC (1671576714000) + Last Successful Execution Date : Never + Error: HTTPConnectionPool(host='10.146.15.207', port=9090): Max retries exceeded with url: /metrics (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + Traceback (most recent call last): + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/urllib3/connection.py", line 174, in _new_conn + conn = connection.create_connection( + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/urllib3/util/connection.py", line 95, in create_connection + raise err + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/urllib3/util/connection.py", line 85, in create_connection + sock.connect(sa) + ConnectionRefusedError: [Errno 111] Connection refused + + During handling of the above exception, another exception occurred: + + Traceback (most recent call last): + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/urllib3/connectionpool.py", line 703, in urlopen + httplib_response = self._make_request( + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/urllib3/connectionpool.py", line 398, in _make_request + conn.request(method, url, **httplib_request_kw) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/urllib3/connection.py", line 239, in request + super(HTTPConnection, self).request(method, url, body=body, headers=headers) + File "/opt/datadog-agent/embedded/lib/python3.8/http/client.py", line 1256, in request + self._send_request(method, url, body, headers, encode_chunked) + File "/opt/datadog-agent/embedded/lib/python3.8/http/client.py", line 1302, in _send_request + self.endheaders(body, encode_chunked=encode_chunked) + File "/opt/datadog-agent/embedded/lib/python3.8/http/client.py", line 1251, in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + File "/opt/datadog-agent/embedded/lib/python3.8/http/client.py", line 1011, in _send_output + self.send(msg) + File "/opt/datadog-agent/embedded/lib/python3.8/http/client.py", line 951, in send + self.connect() + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/urllib3/connection.py", line 205, in connect + conn = self._new_conn() + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/urllib3/connection.py", line 186, in _new_conn + raise NewConnectionError( + urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused + + During handling of the above exception, another exception occurred: + + Traceback (most recent call last): + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/requests/adapters.py", line 489, in send + resp = conn.urlopen( + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/urllib3/connectionpool.py", line 787, in urlopen + retries = retries.increment( + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/urllib3/util/retry.py", line 592, in increment + raise MaxRetryError(_pool, url, error or ResponseError(cause)) + urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='10.146.15.207', port=9090): Max retries exceeded with url: /metrics (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + During handling of the above exception, another exception occurred: + + Traceback (most recent call last): + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/base/checks/base.py", line 1122, in run + self.check(instance) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/base/checks/openmetrics/base_check.py", line 142, in check + self.process(scraper_config) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/base/checks/openmetrics/mixins.py", line 573, in process + for metric in self.scrape_metrics(scraper_config): + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/base/checks/openmetrics/mixins.py", line 500, in scrape_metrics + response = self.poll(scraper_config) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/base/checks/openmetrics/mixins.py", line 837, in poll + response = self.send_request(endpoint, scraper_config, headers) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/base/checks/openmetrics/mixins.py", line 863, in send_request + return http_handler.get(endpoint, stream=True, **kwargs) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/base/utils/http.py", line 356, in get + return self._request('get', url, options) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/base/utils/http.py", line 420, in _request + response = self.make_request_aia_chasing(request_method, method, url, new_options, persist) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/base/utils/http.py", line 426, in make_request_aia_chasing + response = request_method(url, **new_options) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/requests/api.py", line 73, in get + return request("get", url, params=params, **kwargs) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/requests/api.py", line 59, in request + return session.request(method=method, url=url, **kwargs) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/requests/sessions.py", line 587, in request + resp = self.send(prep, **send_kwargs) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/requests/sessions.py", line 701, in send + r = adapter.send(request, **kwargs) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/requests/adapters.py", line 565, in send + raise ConnectionError(e, request=request) + requests.exceptions.ConnectionError: HTTPConnectionPool(host='10.146.15.207', port=9090): Max retries exceeded with url: /metrics (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + datadog_cluster_agent (2.4.0) + ----------------------------- + Instance ID: datadog_cluster_agent:4b0f56c49d48c92e [OK] + Configuration Source: file:/etc/datadog-agent/conf.d/datadog_cluster_agent.d/auto_conf.yaml + Total Runs: 842 + Metric Samples: Last Run: 125, Total: 104,832 + Events: Last Run: 0, Total: 0 + Service Checks: Last Run: 1, Total: 842 + Average Execution Time : 29ms + Last Execution Date : 2022-12-20 22:52:01 UTC (1671576721000) + Last Successful Execution Date : 2022-12-20 22:52:01 UTC (1671576721000) + + Instance ID: datadog_cluster_agent:79dc7329a0398f09 [OK] + Configuration Source: file:/etc/datadog-agent/conf.d/datadog_cluster_agent.d/auto_conf.yaml + Total Runs: 838 + Metric Samples: Last Run: 61, Total: 50,672 + Events: Last Run: 0, Total: 0 + Service Checks: Last Run: 1, Total: 838 + Average Execution Time : 25ms + Last Execution Date : 2022-12-20 22:51:59 UTC (1671576719000) + Last Successful Execution Date : 2022-12-20 22:51:59 UTC (1671576719000) + + + network (2.9.2) + --------------- + Instance ID: network:d884b5186b651429 [OK] + Configuration Source: file:/etc/datadog-agent/conf.d/network.d/conf.yaml.default + Total Runs: 841 + Metric Samples: Last Run: 174, Total: 146,334 + Events: Last Run: 0, Total: 0 + Service Checks: Last Run: 0, Total: 0 + Average Execution Time : 6ms + Last Execution Date : 2022-12-20 22:51:48 UTC (1671576708000) + Last Successful Execution Date : 2022-12-20 22:51:48 UTC (1671576708000) + + Check Initialization Errors + =========================== + + + postgres (13.1.0) + ----------------- + + instance 0: + + could not invoke 'postgres' python check constructor. New constructor API returned: +Traceback (most recent call last): + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/postgres/postgres.py", line 62, in __init__ + self._config = PostgresConfig(self.instance) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/postgres/config.py", line 35, in __init__ + raise ConfigurationError('Please specify a user to connect to Postgres.') +datadog_checks.base.errors.ConfigurationError: Please specify a user to connect to Postgres. +Deprecated constructor API returned: +__init__() got an unexpected keyword argument 'agentConfig' + Loading Errors + ============== + postgres + -------- + Core Check Loader: + Check postgres not found in Catalog + + JMX Check Loader: + check is not a jmx check, or unable to determine if it's so + + Python Check Loader: + could not configure check instance for python check postgres: could not invoke 'postgres' python check constructor. New constructor API returned: +Traceback (most recent call last): + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/postgres/postgres.py", line 62, in __init__ + self._config = PostgresConfig(self.instance) + File "/opt/datadog-agent/embedded/lib/python3.8/site-packages/datadog_checks/postgres/config.py", line 35, in __init__ + raise ConfigurationError('Please specify a user to connect to Postgres.') +datadog_checks.base.errors.ConfigurationError: Please specify a user to connect to Postgres. +Deprecated constructor API returned: +__init__() got an unexpected keyword argument 'agentConfig' + +======== +JMXFetch +======== + + Information + ================== + Initialized checks + ================== + no checks + + Failed checks + ============= + no checks + +========= +Forwarder +========= + + Transactions + ============ + Cluster: 0 + ClusterRole: 0 + ClusterRoleBinding: 0 + CronJob: 0 + DaemonSet: 0 + Deployment: 0 + Dropped: 0 + HighPriorityQueueFull: 0 + Ingress: 0 + Job: 0 + Namespace: 0 + Node: 0 + PersistentVolume: 0 + PersistentVolumeClaim: 0 + Pod: 0 + ReplicaSet: 0 + Requeued: 0 + Retried: 0 + RetryQueueSize: 0 + Role: 0 + RoleBinding: 0 + Service: 0 + ServiceAccount: 0 + StatefulSet: 0 + + Transaction Successes + ===================== + Total number: 1775 + Successes By Endpoint: + check_run_v1: 841 + intake: 72 + metadata_v1: 21 + series_v2: 841 + + On-disk storage + =============== + On-disk storage is disabled. Configure `forwarder_storage_max_size_in_bytes` to enable it. + + API Keys status + =============== + API key ending with 841ae: API Key valid + +========== +Endpoints +========== + https://app.datadoghq.com - API Key ending with: + - 841ae + +========== +Logs Agent +========== + + Reliable: Sending uncompressed logs in SSL encrypted TCP to agent-intake.logs.datadoghq.com on port 10516 + + You are currently sending Logs to Datadog through TCP (either because logs_config.force_use_tcp or logs_config.socks5_proxy_address is set or the HTTP connectivity test has failed). To benefit from increased reliability and better network performances, we strongly encourage switching over to compressed HTTPS which is now the default protocol. + + BytesSent: 1.8474997e+07 + EncodedBytesSent: 1.8474997e+07 + LogsProcessed: 10438 + LogsSent: 10438 + ============ + Integrations + ============ + + kube-system/pdcsi-node-vmxbk/gce-pd-driver + ------------------------------------------ + - Type: file + Identifier: 401a8645147ae8ef2baf2a5187c22b61554a64c5e0800b481c7a6a6e2e5e9d53 + Path: /var/log/pods/kube-system_pdcsi-node-vmxbk_8194ece2-46dd-495e-9220-3a6b88fa4d61/gce-pd-driver/*.log + Service: gcp-compute-persistent-disk-csi-driver + Source: gcp-compute-persistent-disk-csi-driver + Status: OK + 1 files tailed out of 1 files matching + Inputs: + /var/log/pods/kube-system_pdcsi-node-vmxbk_8194ece2-46dd-495e-9220-3a6b88fa4d61/gce-pd-driver/0.log + + kube-system/l7-default-backend-6dc845c45d-xlnmh/default-http-backend + -------------------------------------------------------------------- + - Type: file + Identifier: 0f23fbf70ab6cb8063cacb65bf7c7472a6e4062838764cac256439070942f161 + Path: /var/log/pods/kube-system_l7-default-backend-6dc845c45d-xlnmh_85840891-57e7-4fd4-8c1d-9a7ec5227614/default-http-backend/*.log + Service: ingress-gce-404-server-with-metrics + Source: ingress-gce-404-server-with-metrics + Status: OK + 1 files tailed out of 1 files matching + Inputs: + /var/log/pods/kube-system_l7-default-backend-6dc845c45d-xlnmh_85840891-57e7-4fd4-8c1d-9a7ec5227614/default-http-backend/0.log + + + +============= +Process Agent +============= + + Version: x.y.z + Status date: 2022-12-20 22:52:01.802 UTC (1671576721802) + Process Agent Start: 2022-12-20 19:21:28.069 UTC (1671564088069) + Pid: 12223 + Go Version: go1.18.8 + Build arch: amd64 + Log Level: INFO + Enabled Checks: [container rtcontainer pod] + Allocated Memory: 35,295,544 bytes + Hostname: gke-dd-sandbox-bits-8943422b-5wpg.c.datadog-sandbox.internal + System Probe Process Module Status: Not running + Process Language Detection Enabled: False + + ================= + Process Endpoints + ================= + https://process.datadoghq.com - API Key ending with: + - 841ae + + ========= + Collector + ========= + Last collection time: 2022-12-20 22:51:56 + Docker socket: + Number of processes: 0 + Number of containers: 25 + Process Queue length: 0 + RTProcess Queue length: 0 + Connections Queue length: 0 + Event Queue length: 0 + Pod Queue length: 0 + Process Bytes enqueued: 0 + RTProcess Bytes enqueued: 0 + Connections Bytes enqueued: 0 + Event Bytes enqueued: 0 + Pod Bytes enqueued: 0 + Drop Check Payloads: [] + + ========== + Extractors + ========== + + Workloadmeta + ============ + Cache size: + Stale diffs discarded: + Diffs dropped: + +========= +APM Agent +========= + Status: Running + Pid: 12174 + Uptime: 12633 seconds + Mem alloc: 9,203,488 bytes + Hostname: gke-dd-sandbox-bits-8943422b-5wpg.c.datadog-sandbox.internal + Receiver: 0.0.0.0:8126 + Endpoints: + https://trace.agent.datadoghq.com + + Receiver (previous minute) + ========================== + No traces received in the previous minute. + + + Writer (previous minute) + ======================== + Traces: 0 payloads, 0 traces, 0 events, 0 bytes + Stats: 0 payloads, 0 stats buckets, 0 bytes + +========== +Aggregator +========== + Checks Metric Sample: 3,800,535 + Dogstatsd Metric Sample: 136,758 + Event: 1 + Events Flushed: 1 + Number Of Flushes: 841 + Series Flushed: 3,224,651 + Service Check: 25,700 + Service Checks Flushed: 26,509 + +========= +DogStatsD +========= + Event Packets: 0 + Event Parse Errors: 0 + Metric Packets: 136,757 + Metric Parse Errors: 0 + Service Check Packets: 0 + Service Check Parse Errors: 0 + Udp Bytes: 21,336,530 + Udp Packet Reading Errors: 0 + Udp Packets: 76,232 + Uds Bytes: 0 + Uds Origin Detection Errors: 0 + Uds Packet Reading Errors: 0 + Uds Packets: 1 + Unterminated Metric Errors: 0 + +Tip: For troubleshooting, enable 'dogstatsd_metrics_stats_enable' in the main datadog.yaml file to generate Dogstatsd logs. Once 'dogstatsd_metrics_stats_enable' is enabled, users can also use 'dogstatsd-stats' command to get visibility of the latest collected metrics. +===================== +Datadog Cluster Agent +===================== + + - Datadog Cluster Agent endpoint detected: https://10.122.58.252:5005 + Successfully connected to the Datadog Cluster Agent. + - Running: x.y.z+commit.9b0b54b +========== +SNMP Traps +========== + Packets: 0 + Packets Auth Errors: 0 +============= +Autodiscovery +============= + Enabled Features + ================ + containerd + cri + docker + kubernetes + +==================== +Remote Configuration +==================== + + Remote Configuration is disabled + + +==== +OTLP +==== + + Status: Not enabled + Collector status: Not running + \ No newline at end of file diff --git a/pkg/status/render/fixtures/check_stats.json b/pkg/status/render/fixtures/check_stats.json new file mode 100644 index 0000000000000..ab6a327cef73e --- /dev/null +++ b/pkg/status/render/fixtures/check_stats.json @@ -0,0 +1,539 @@ +{ + "pyLoaderStats": { + "ConfigureErrors": {}, + "Py3Warnings": {} + }, + "pythonInit": { + "Errors": [ + "could not initialize rtloader: error initializing string utils: No module named 'yaml'" + ] + }, + "python_version": "n/a", + "remoteConfiguration": { + "apiKeyScoped": "false", + "lastError": "", + "orgEnabled": "false" + }, + "runnerStats": { + "Checks": { + "cpu": { + "cpu": { + "AverageExecutionTime": 0, + "CheckConfigSource": "file:/opt/datadog-agent/etc/conf.d/cpu.d/conf.yaml.default", + "CheckID": "cpu", + "CheckName": "cpu", + "CheckVersion": "", + "EventPlatformEvents": {}, + "Events": 0, + "ExecutionTimes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0 + ], + "HistogramBuckets": 0, + "LastError": "", + "LastExecutionTime": 0, + "LastSuccessDate": 1701259541, + "LastWarnings": [], + "MetricSamples": 8, + "ServiceChecks": 0, + "TotalErrors": 0, + "TotalEventPlatformEvents": {}, + "TotalEvents": 0, + "TotalHistogramBuckets": 0, + "TotalMetricSamples": 985, + "TotalRuns": 124, + "TotalServiceChecks": 0, + "TotalWarnings": 0, + "UpdateTimestamp": 1701259541 + } + }, + "disk": { + "disk": { + "AverageExecutionTime": 1, + "CheckConfigSource": "file:/opt/datadog-agent/etc/conf.d/disk.d/conf.yaml.default", + "CheckID": "disk", + "CheckName": "disk", + "CheckVersion": "", + "EventPlatformEvents": {}, + "Events": 0, + "ExecutionTimes": [ + 0, + 2, + 0, + 0, + 4, + 1, + 1, + 1, + 2, + 1, + 4, + 1, + 1, + 2, + 3, + 1, + 2, + 1, + 1, + 0, + 2, + 2, + 4, + 2, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 1 + ], + "HistogramBuckets": 0, + "LastError": "", + "LastExecutionTime": 2, + "LastSuccessDate": 1701259533, + "LastWarnings": [], + "MetricSamples": 84, + "ServiceChecks": 0, + "TotalErrors": 0, + "TotalEventPlatformEvents": {}, + "TotalEvents": 0, + "TotalHistogramBuckets": 0, + "TotalMetricSamples": 10332, + "TotalRuns": 123, + "TotalServiceChecks": 0, + "TotalWarnings": 0, + "UpdateTimestamp": 1701259533 + } + }, + "file_handle": { + "file_handle": { + "AverageExecutionTime": 0, + "CheckConfigSource": "file:/Users/gustavo.caso/go/src/github.com/DataDog/datadog-agent/bin/agent/dist/conf.d/file_handle.d/conf.yaml.default", + "CheckID": "file_handle", + "CheckName": "file_handle", + "CheckVersion": "", + "EventPlatformEvents": {}, + "Events": 0, + "ExecutionTimes": [ + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 5, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 2, + 1, + 0, + 0 + ], + "HistogramBuckets": 0, + "LastError": "open /proc/sys/fs/file-nr: no such file or directory", + "LastExecutionTime": 0, + "LastSuccessDate": 0, + "LastWarnings": [], + "MetricSamples": 0, + "ServiceChecks": 0, + "TotalErrors": 123, + "TotalEventPlatformEvents": {}, + "TotalEvents": 0, + "TotalHistogramBuckets": 0, + "TotalMetricSamples": 0, + "TotalRuns": 123, + "TotalServiceChecks": 0, + "TotalWarnings": 0, + "UpdateTimestamp": 1701259538 + } + }, + "io": { + "io": { + "AverageExecutionTime": 0, + "CheckConfigSource": "file:/opt/datadog-agent/etc/conf.d/io.d/conf.yaml.default", + "CheckID": "io", + "CheckName": "io", + "CheckVersion": "", + "EventPlatformEvents": {}, + "Events": 0, + "ExecutionTimes": [ + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 4, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1 + ], + "HistogramBuckets": 0, + "LastError": "", + "LastExecutionTime": 1, + "LastSuccessDate": 1701259540, + "LastWarnings": [], + "MetricSamples": 15, + "ServiceChecks": 0, + "TotalErrors": 0, + "TotalEventPlatformEvents": {}, + "TotalEvents": 0, + "TotalHistogramBuckets": 0, + "TotalMetricSamples": 1836, + "TotalRuns": 123, + "TotalServiceChecks": 0, + "TotalWarnings": 0, + "UpdateTimestamp": 1701259540 + } + }, + "load": { + "load": { + "AverageExecutionTime": 0, + "CheckConfigSource": "file:/opt/datadog-agent/etc/conf.d/load.d/conf.yaml.default", + "CheckID": "load", + "CheckName": "load", + "CheckVersion": "", + "EventPlatformEvents": {}, + "Events": 0, + "ExecutionTimes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "HistogramBuckets": 0, + "LastError": "", + "LastExecutionTime": 0, + "LastSuccessDate": 1701259532, + "LastWarnings": [], + "MetricSamples": 6, + "ServiceChecks": 0, + "TotalErrors": 0, + "TotalEventPlatformEvents": {}, + "TotalEvents": 0, + "TotalHistogramBuckets": 0, + "TotalMetricSamples": 738, + "TotalRuns": 123, + "TotalServiceChecks": 0, + "TotalWarnings": 0, + "UpdateTimestamp": 1701259532 + } + }, + "memory": { + "memory": { + "AverageExecutionTime": 0, + "CheckConfigSource": "file:/opt/datadog-agent/etc/conf.d/memory.d/conf.yaml.default", + "CheckID": "memory", + "CheckName": "memory", + "CheckVersion": "", + "EventPlatformEvents": {}, + "Events": 0, + "ExecutionTimes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0 + ], + "HistogramBuckets": 0, + "LastError": "", + "LastExecutionTime": 0, + "LastSuccessDate": 1701259539, + "LastWarnings": [], + "MetricSamples": 11, + "ServiceChecks": 0, + "TotalErrors": 0, + "TotalEventPlatformEvents": {}, + "TotalEvents": 0, + "TotalHistogramBuckets": 0, + "TotalMetricSamples": 1353, + "TotalRuns": 123, + "TotalServiceChecks": 0, + "TotalWarnings": 0, + "UpdateTimestamp": 1701259539 + } + }, + "ntp": { + "ntp:3c427a42a70bbf8": { + "AverageExecutionTime": 172, + "CheckConfigSource": "file:/opt/datadog-agent/etc/conf.d/ntp.d/conf.yaml.default", + "CheckID": "ntp:3c427a42a70bbf8", + "CheckName": "ntp", + "CheckVersion": "", + "EventPlatformEvents": {}, + "Events": 0, + "ExecutionTimes": [ + 143, + 187, + 186, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "HistogramBuckets": 0, + "LastError": "", + "LastExecutionTime": 186, + "LastSuccessDate": 1701259498, + "LastWarnings": [], + "MetricSamples": 1, + "ServiceChecks": 1, + "TotalErrors": 0, + "TotalEventPlatformEvents": {}, + "TotalEvents": 0, + "TotalHistogramBuckets": 0, + "TotalMetricSamples": 3, + "TotalRuns": 3, + "TotalServiceChecks": 3, + "TotalWarnings": 0, + "UpdateTimestamp": 1701259498 + } + }, + "uptime": { + "uptime": { + "AverageExecutionTime": 0, + "CheckConfigSource": "file:/opt/datadog-agent/etc/conf.d/uptime.d/conf.yaml.default", + "CheckID": "uptime", + "CheckName": "uptime", + "CheckVersion": "", + "EventPlatformEvents": {}, + "Events": 0, + "ExecutionTimes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "HistogramBuckets": 0, + "LastError": "", + "LastExecutionTime": 0, + "LastSuccessDate": 1701259546, + "LastWarnings": [], + "MetricSamples": 1, + "ServiceChecks": 0, + "TotalErrors": 0, + "TotalEventPlatformEvents": {}, + "TotalEvents": 0, + "TotalHistogramBuckets": 0, + "TotalMetricSamples": 124, + "TotalRuns": 124, + "TotalServiceChecks": 0, + "TotalWarnings": 0, + "UpdateTimestamp": 1701259546 + } + } + }, + "Errors": 123, + "Running": { + "container_image": "2023-11-29T12:34:55+01:00", + "container_lifecycle": "2023-11-29T12:34:55+01:00" + }, + "RunningChecks": 2, + "Runs": 866, + "Workers": { + "Count": 6, + "Instances": { + "worker_1": { + "Utilization": 1 + }, + "worker_2": { + "Utilization": 1 + }, + "worker_3": { + "Utilization": 0 + }, + "worker_4": { + "Utilization": 0 + }, + "worker_5": { + "Utilization": 0 + }, + "worker_6": { + "Utilization": 0 + } + } + } + } +} diff --git a/pkg/status/render/fixtures/check_stats.text b/pkg/status/render/fixtures/check_stats.text new file mode 100755 index 0000000000000..c7cf3e7084b9c --- /dev/null +++ b/pkg/status/render/fixtures/check_stats.text @@ -0,0 +1,115 @@ +========= +Collector +========= + Error initializing Python + ========================= + - could not initialize rtloader: error initializing string utils: No module named 'yaml' + + + Running Checks + ============== + + cpu + --- + Instance ID: cpu [OK] + Configuration Source: file:/opt/datadog-agent/etc/conf.d/cpu.d/conf.yaml.default + Total Runs: 124 + Metric Samples: Last Run: 8, Total: 985 + Events: Last Run: 0, Total: 0 + Service Checks: Last Run: 0, Total: 0 + Average Execution Time : 0s + Last Execution Date : 2023-11-29 12:05:41 UTC (1701259541000) + Last Successful Execution Date : 2023-11-29 12:05:41 UTC (1701259541000) + + + disk + ---- + Instance ID: disk [OK] + Configuration Source: file:/opt/datadog-agent/etc/conf.d/disk.d/conf.yaml.default + Total Runs: 123 + Metric Samples: Last Run: 84, Total: 10,332 + Events: Last Run: 0, Total: 0 + Service Checks: Last Run: 0, Total: 0 + Average Execution Time : 1ms + Last Execution Date : 2023-11-29 12:05:33 UTC (1701259533000) + Last Successful Execution Date : 2023-11-29 12:05:33 UTC (1701259533000) + + + file_handle + ----------- + Instance ID: file_handle [ERROR] + Configuration Source: file:/Users/gustavo.caso/go/src/github.com/DataDog/datadog-agent/bin/agent/dist/conf.d/file_handle.d/conf.yaml.default + Total Runs: 123 + Metric Samples: Last Run: 0, Total: 0 + Events: Last Run: 0, Total: 0 + Service Checks: Last Run: 0, Total: 0 + Average Execution Time : 0s + Last Execution Date : 2023-11-29 12:05:38 UTC (1701259538000) + Last Successful Execution Date : Never + Error: open /proc/sys/fs/file-nr: no such file or directory + No traceback + + io + -- + Instance ID: io [OK] + Configuration Source: file:/opt/datadog-agent/etc/conf.d/io.d/conf.yaml.default + Total Runs: 123 + Metric Samples: Last Run: 15, Total: 1,836 + Events: Last Run: 0, Total: 0 + Service Checks: Last Run: 0, Total: 0 + Average Execution Time : 0s + Last Execution Date : 2023-11-29 12:05:40 UTC (1701259540000) + Last Successful Execution Date : 2023-11-29 12:05:40 UTC (1701259540000) + + + load + ---- + Instance ID: load [OK] + Configuration Source: file:/opt/datadog-agent/etc/conf.d/load.d/conf.yaml.default + Total Runs: 123 + Metric Samples: Last Run: 6, Total: 738 + Events: Last Run: 0, Total: 0 + Service Checks: Last Run: 0, Total: 0 + Average Execution Time : 0s + Last Execution Date : 2023-11-29 12:05:32 UTC (1701259532000) + Last Successful Execution Date : 2023-11-29 12:05:32 UTC (1701259532000) + + + memory + ------ + Instance ID: memory [OK] + Configuration Source: file:/opt/datadog-agent/etc/conf.d/memory.d/conf.yaml.default + Total Runs: 123 + Metric Samples: Last Run: 11, Total: 1,353 + Events: Last Run: 0, Total: 0 + Service Checks: Last Run: 0, Total: 0 + Average Execution Time : 0s + Last Execution Date : 2023-11-29 12:05:39 UTC (1701259539000) + Last Successful Execution Date : 2023-11-29 12:05:39 UTC (1701259539000) + + + ntp + --- + Instance ID: ntp:3c427a42a70bbf8 [OK] + Configuration Source: file:/opt/datadog-agent/etc/conf.d/ntp.d/conf.yaml.default + Total Runs: 3 + Metric Samples: Last Run: 1, Total: 3 + Events: Last Run: 0, Total: 0 + Service Checks: Last Run: 1, Total: 3 + Average Execution Time : 172ms + Last Execution Date : 2023-11-29 12:04:58 UTC (1701259498000) + Last Successful Execution Date : 2023-11-29 12:04:58 UTC (1701259498000) + + + uptime + ------ + Instance ID: uptime [OK] + Configuration Source: file:/opt/datadog-agent/etc/conf.d/uptime.d/conf.yaml.default + Total Runs: 124 + Metric Samples: Last Run: 1, Total: 124 + Events: Last Run: 0, Total: 0 + Service Checks: Last Run: 0, Total: 0 + Average Execution Time : 0s + Last Execution Date : 2023-11-29 12:05:46 UTC (1701259546000) + Last Successful Execution Date : 2023-11-29 12:05:46 UTC (1701259546000) + diff --git a/pkg/status/render/fixtures/cluster_agent_status.json b/pkg/status/render/fixtures/cluster_agent_status.json new file mode 100644 index 0000000000000..c1415f2fb8291 --- /dev/null +++ b/pkg/status/render/fixtures/cluster_agent_status.json @@ -0,0 +1,730 @@ +{ + "adEnabledFeatures": {}, + "admissionWebhook": { + "Error": "temporary failure in apiserver, will retry later: try delay not elapsed yet" + }, + "agent_metadata": {}, + "agent_start_nano": 1701185796319735000, + "aggregatorStats": { + "ChecksHistogramBucketMetricSample": 0, + "ChecksMetricSample": 0, + "DogstatsdContexts": 0, + "DogstatsdContextsByMtype": { + "Count": 0, + "Counter": 0, + "Distribution": 0, + "Gauge": 0, + "Histogram": 0, + "Historate": 0, + "MonotonicCount": 0, + "Rate": 0, + "Set": 0 + }, + "DogstatsdMetricSample": 0, + "Event": 0, + "EventPlatformEvents": {}, + "EventPlatformEventsErrors": {}, + "EventsFlushErrors": 0, + "EventsFlushed": 0, + "Flush": { + "ChecksMetricSampleFlushTime": { + "FlushIndex": 12, + "Flushes": [ + 1768375, + 2815000, + 8566000, + 1876667, + 1271833, + 2088333, + 1546667, + 436833, + 2543250, + 2415959, + 1499292, + 2315875, + 1433541, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 1433541, + "Name": "ChecksMetricSampleFlushTime" + }, + "EventFlushTime": { + "FlushIndex": -1, + "Flushes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 0, + "Name": "EventFlushTime" + }, + "MainFlushTime": { + "FlushIndex": 12, + "Flushes": [ + 1784125, + 2821542, + 8623750, + 1885750, + 1277792, + 2093125, + 1551875, + 444375, + 2555167, + 2428709, + 1508584, + 2322458, + 1441916, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 1441916, + "Name": "MainFlushTime" + }, + "ManifestsTime": { + "FlushIndex": -1, + "Flushes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 0, + "Name": "ManifestsTime" + }, + "MetricSketchFlushTime": { + "FlushIndex": -1, + "Flushes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 0, + "Name": "MetricSketchFlushTime" + }, + "ServiceCheckFlushTime": { + "FlushIndex": 12, + "Flushes": [ + 2210709, + 1744708, + 9220916, + 1271000, + 1261292, + 1699291, + 1536875, + 585291, + 2765334, + 2870625, + 1489667, + 2283125, + 1386500, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 1386500, + "Name": "ServiceCheckFlushTime" + } + }, + "FlushCount": { + "Events": { + "FlushIndex": -1, + "Flushes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 0, + "Name": "Events" + }, + "Manifests": { + "FlushIndex": -1, + "Flushes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 0, + "Name": "Manifests" + }, + "Series": { + "FlushIndex": 12, + "Flushes": [ + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 2, + "Name": "Series" + }, + "ServiceChecks": { + "FlushIndex": 12, + "Flushes": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 1, + "Name": "ServiceChecks" + }, + "Sketches": { + "FlushIndex": -1, + "Flushes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 0, + "Name": "Sketches" + } + }, + "HostnameUpdate": 0, + "MetricTags": { + "Series": { + "Above100": 0, + "Above90": 0 + }, + "Sketches": { + "Above100": 0, + "Above90": 0 + } + }, + "NumberOfFlush": 13, + "OrchestratorManifests": 0, + "OrchestratorManifestsErrors": 0, + "OrchestratorMetadata": 0, + "OrchestratorMetadataErrors": 0, + "SeriesFlushErrors": 0, + "SeriesFlushed": 26, + "ServiceCheck": 0, + "ServiceCheckFlushErrors": 0, + "ServiceCheckFlushed": 13, + "SketchesFlushErrors": 0, + "SketchesFlushed": 0 + }, + "autoConfigStats": { + "ConfigErrors": {}, + "ResolveWarnings": {} + }, + "build_arch": "arm64", + "checkSchedulerStats": { + "LoaderErrors": {}, + "RunErrors": {} + }, + "complianceChecks": null, + "conf_file": "dev/dist/datadog.yaml", + "config": { + "confd_path": "/opt/datadog-agent/etc/conf.d", + "log_level": "info" + }, + "custommetrics": { + "Error": "temporary failure in apiserver, will retry later: try delay not elapsed yet" + }, + "dogstatsdStats": { + "UdpBytes": 0, + "UdpPacketReadingErrors": 0, + "UdpPackets": 0, + "UdsBytes": 0, + "UdsOriginDetectionErrors": 0, + "UdsPacketReadingErrors": 0, + "UdsPackets": 0 + }, + "endpointsInfos": { + "https://app.datadoghq.eu": [ + "72724" + ] + }, + "externalmetrics": {}, + "filterErrors": {}, + "flavor": "cluster_agent", + "forwarderStats": { + "APIKeyFailure": {}, + "APIKeyStatus": {}, + "FileStorage": { + "CurrentSizeInBytes": 0, + "DeserializeCount": 0, + "DeserializeErrorsCount": 0, + "DeserializeTransactionsCount": 0, + "FileSize": 0, + "FilesCount": 0, + "FilesRemovedCount": 0, + "PointsDroppedCount": 0, + "SerializeCount": 0, + "StartupReloadedRetryFilesCount": 0 + }, + "RemovalPolicy": { + "FilesFromUnknownDomainCount": 0, + "NewRemovalPolicyCount": 0, + "OutdatedFilesCount": 0, + "RegisteredDomainCount": 0 + }, + "TransactionContainer": { + "CurrentMemSizeInBytes": 0, + "ErrorsCount": 0, + "PointsDroppedCount": 0, + "TransactionsCount": 0, + "TransactionsDroppedCount": 0 + }, + "Transactions": { + "Cluster": 0, + "ClusterRole": 0, + "ClusterRoleBinding": 0, + "ConnectionEvents": { + "ConnectSuccess": 1, + "DNSSuccess": 1 + }, + "CronJob": 0, + "CustomResource": 0, + "CustomResourceDefinition": 0, + "DaemonSet": 0, + "Deployment": 0, + "Dropped": 0, + "DroppedByEndpoint": {}, + "Errors": 0, + "ErrorsByType": { + "ConnectionErrors": 0, + "DNSErrors": 0, + "SentRequestErrors": 0, + "TLSErrors": 0, + "WroteRequestErrors": 0 + }, + "HTTPErrors": 0, + "HTTPErrorsByCode": {}, + "HighPriorityQueueFull": 0, + "HorizontalPodAutoscaler": 0, + "Ingress": 0, + "InputBytesByEndpoint": { + "check_run_v1": 1672, + "series_v2": 2456 + }, + "InputCountByEndpoint": { + "check_run_v1": 13, + "series_v2": 13 + }, + "Job": 0, + "Namespace": 0, + "Node": 0, + "OrchestratorManifest": 0, + "PersistentVolume": 0, + "PersistentVolumeClaim": 0, + "Pod": 0, + "ReplicaSet": 0, + "Requeued": 0, + "RequeuedByEndpoint": {}, + "Retried": 0, + "RetriedByEndpoint": {}, + "RetryQueueSize": 0, + "Role": 0, + "RoleBinding": 0, + "Service": 0, + "ServiceAccount": 0, + "StatefulSet": 0, + "Success": 26, + "SuccessByEndpoint": { + "check_run_v1": 13, + "connections": 0, + "container": 0, + "events_v2": 0, + "host_metadata_v2": 0, + "intake": 0, + "orchestrator": 0, + "process": 0, + "rtcontainer": 0, + "rtprocess": 0, + "series_v1": 0, + "series_v2": 13, + "services_checks_v2": 0, + "sketches_v1": 0, + "sketches_v2": 0, + "validate_v1": 0 + }, + "SuccessBytesByEndpoint": { + "check_run_v1": 1672, + "series_v2": 2456 + }, + "VerticalPodAutoscaler": 0 + } + }, + "go_version": "go1.20.11", + "hostnameStats": { + "errors": { + "'hostname' configuration/environment": "hostname is empty", + "'hostname_file' configuration/environment": "'hostname_file' configuration is not enabled", + "aws": "not retrieving hostname from AWS: the host is not an ECS instance and other providers already retrieve non-default hostnames", + "azure": "azure_hostname_style is set to 'os'", + "container": "the agent is not containerized", + "fargate": "agent is not runnning on Fargate", + "fqdn": "'hostname_fqdn' configuration is not enabled", + "gce": "unable to retrieve hostname from GCE: GCE metadata API error: Get \"http://169.254.169.254/computeMetadata/v1/instance/hostname\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" + }, + "provider": "os" + }, + "inventories": {}, + "leaderelection": { + "error": "temporary failure in apiserver, will retry later: try delay not elapsed yet", + "status": "Failing" + }, + "metadata": { + "os": "darwin", + "agent-flavor": "cluster_agent", + "python": "n/a", + "systemStats": { + "cpuCores": 10, + "machine": "arm64", + "platform": "darwin", + "pythonV": "n/a", + "processor": "Apple M1 Max", + "macV": [ + "13.6.2", + [ + "", + "", + "" + ], + "arm64" + ], + "nixV": [ + "", + "", + "" + ], + "fbsdV": [ + "", + "", + "" + ], + "winV": [ + "", + "", + "" + ] + }, + "meta": { + "socket-hostname": "COMP-VQHPF4W6GY", + "timezones": [ + "CET" + ], + "socket-fqdn": "COMP-VQHPF4W6GY", + "ec2-hostname": "", + "hostname": "COMP-VQHPF4W6GY", + "host_aliases": [], + "instance-id": "" + }, + "host-tags": { + "system": [] + }, + "network": null, + "logs": { + "transport": "", + "auto_multi_line_detection_enabled": false + }, + "install-method": { + "tool": null, + "tool_version": "undefined", + "installer_version": null + }, + "proxy-info": { + "no-proxy-nonexact-match": false, + "proxy-behavior-changed": false, + "no-proxy-nonexact-match-explicitly-set": false + }, + "otlp": { + "enabled": false + } + }, + "ntpOffset": 0, + "pid": 73895, + "pyLoaderStats": null, + "pythonInit": null, + "runnerStats": { + "Checks": {}, + "Running": {}, + "Workers": { + "Count": 0, + "Instances": {} + } + }, + "time_nano": 1701186004251999000, + "version": "7.50.0-devel+git.813.61fc133" +} diff --git a/pkg/status/render/fixtures/cluster_agent_status.text b/pkg/status/render/fixtures/cluster_agent_status.text new file mode 100755 index 0000000000000..a13e26583d7fe --- /dev/null +++ b/pkg/status/render/fixtures/cluster_agent_status.text @@ -0,0 +1,123 @@ + +===================================================== +Datadog Cluster Agent (v7.50.0-devel+git.813.61fc133) +===================================================== + + Status date: 2023-11-28 15:40:04.251 UTC (1701186004251) + Agent start: 2023-11-28 15:36:36.319 UTC (1701185796319) + Pid: 73895 + Go Version: go1.20.11 + Build arch: arm64 + Agent flavor: cluster_agent + Log Level: info + + Paths + ===== + Config File: dev/dist/datadog.yaml + conf.d: /opt/datadog-agent/etc/conf.d + + Clocks + ====== + System time: 2023-11-28 15:40:04.251 UTC (1701186004251) + + Hostnames + ========= + hostname: COMP-VQHPF4W6GY + socket-fqdn: COMP-VQHPF4W6GY + socket-hostname: COMP-VQHPF4W6GY + hostname provider: os + unused hostname providers: + 'hostname' configuration/environment: hostname is empty + 'hostname_file' configuration/environment: 'hostname_file' configuration is not enabled + aws: not retrieving hostname from AWS: the host is not an ECS instance and other providers already retrieve non-default hostnames + azure: azure_hostname_style is set to 'os' + container: the agent is not containerized + fargate: agent is not runnning on Fargate + fqdn: 'hostname_fqdn' configuration is not enabled + gce: unable to retrieve hostname from GCE: GCE metadata API error: Get "http://169.254.169.254/computeMetadata/v1/instance/hostname": context deadline exceeded (Client.Timeout exceeded while awaiting headers) + + Metadata + ======== + +Leader Election +=============== + Leader Election Status: Failing + Error: temporary failure in apiserver, will retry later: try delay not elapsed yet + + +Custom Metrics Server +===================== + Error: temporary failure in apiserver, will retry later: try delay not elapsed yet + +Admission Controller +==================== + Error: temporary failure in apiserver, will retry later: try delay not elapsed yet + + +========= +Collector +========= + + Running Checks + ============== + No checks have run yet + +========= +Forwarder +========= + + Transactions + ============ + Cluster: 0 + ClusterRole: 0 + ClusterRoleBinding: 0 + CronJob: 0 + CustomResource: 0 + CustomResourceDefinition: 0 + DaemonSet: 0 + Deployment: 0 + Dropped: 0 + HighPriorityQueueFull: 0 + HorizontalPodAutoscaler: 0 + Ingress: 0 + Job: 0 + Namespace: 0 + Node: 0 + OrchestratorManifest: 0 + PersistentVolume: 0 + PersistentVolumeClaim: 0 + Pod: 0 + ReplicaSet: 0 + Requeued: 0 + Retried: 0 + RetryQueueSize: 0 + Role: 0 + RoleBinding: 0 + Service: 0 + ServiceAccount: 0 + StatefulSet: 0 + VerticalPodAutoscaler: 0 + + Transaction Successes + ===================== + Total number: 26 + Successes By Endpoint: + check_run_v1: 13 + series_v2: 13 + + On-disk storage + =============== + On-disk storage is disabled. Configure `forwarder_storage_max_size_in_bytes` to enable it. + +========== +Endpoints +========== + https://app.datadoghq.eu - API Key ending with: + - 72724 + +========== +Logs Agent +========== + + + diff --git a/pkg/status/render/fixtures/process_agent_status.json b/pkg/status/render/fixtures/process_agent_status.json new file mode 100644 index 0000000000000..7d256b9b4430f --- /dev/null +++ b/pkg/status/render/fixtures/process_agent_status.json @@ -0,0 +1,129 @@ +{ + "processAgentStatus": { + "core": { + "build_arch": "arm64", + "config": { + "log_level": "info" + }, + "go_version": "go1.20.10", + "metadata": { + "agent-flavor": "process_agent", + "container-meta": { + "docker_swarm": "inactive", + "docker_version": "24.0.6" + }, + "host-tags": { + "system": [] + }, + "install-method": { + "installer_version": "docker", + "tool": "docker", + "tool_version": "docker" + }, + "logs": { + "auto_multi_line_detection_enabled": false, + "transport": "" + }, + "meta": { + "ec2-hostname": "", + "host_aliases": [], + "hostname": "docker-desktop", + "instance-id": "", + "socket-fqdn": "52254030ab59", + "socket-hostname": "52254030ab59", + "timezones": [ + "UTC" + ] + }, + "network": null, + "os": "linux", + "otlp": { + "enabled": false + }, + "proxy-info": { + "no-proxy-nonexact-match": false, + "no-proxy-nonexact-match-explicitly-set": false, + "proxy-behavior-changed": false + }, + "python": "n/a", + "systemStats": { + "cpuCores": 1, + "fbsdV": [ + "", + "", + "" + ], + "macV": [ + "", + "", + "" + ], + "machine": "arm64", + "nixV": [ + "ubuntu", + "23.04", + "" + ], + "platform": "linux", + "processor": "", + "pythonV": "n/a", + "winV": [ + "", + "", + "" + ] + } + }, + "version": "7.49.1" + }, + "date": 1701254547319371000, + "expvars": { + "connections_queue_bytes": 0, + "connections_queue_size": 0, + "container_count": 1, + "container_id": "52254030ab598053a03b9bd474ac11d72486f640def81fc7be38ed3a5e864df3", + "docker_socket": "/var/run/docker.sock", + "drop_check_payloads": [], + "enabled_checks": [ + "rtcontainer", + "container", + "process_discovery" + ], + "endpoints": { + "https://process.datadoghq.eu": [ + "72724" + ] + }, + "event_queue_bytes": 0, + "event_queue_size": 0, + "language_detection_enabled": false, + "last_collect_time": "2023-11-29 10:42:25", + "log_file": "", + "memstats": { + "alloc": 14060976 + }, + "pid": 23863, + "pod_queue_bytes": 0, + "pod_queue_size": 0, + "process_count": 0, + "process_queue_bytes": 0, + "process_queue_size": 0, + "proxy_url": "", + "rtprocess_queue_bytes": 0, + "rtprocess_queue_size": 0, + "system_probe_process_module_enabled": false, + "uptime": 1494, + "uptime_nano": 1701253052422544600, + "version": { + "BuildDate": "", + "GitBranch": "", + "GitCommit": "", + "GoVersion": "", + "Version": "" + }, + "workloadmeta_extractor_cache_size": 0, + "workloadmeta_extractor_diffs_dropped": 0, + "workloadmeta_extractor_stale_diffs": 0 + } + } +} diff --git a/pkg/status/render/fixtures/process_agent_status.text b/pkg/status/render/fixtures/process_agent_status.text new file mode 100755 index 0000000000000..583358471eab3 --- /dev/null +++ b/pkg/status/render/fixtures/process_agent_status.text @@ -0,0 +1,52 @@ + +============= +Process Agent +============= + + Version: 7.49.1 + Status date: 2023-11-29 10:42:27.319 UTC (1701254547319) + Process Agent Start: 2023-11-29 10:17:32.422 UTC (1701253052422) + Pid: 23863 + Go Version: go1.20.10 + Build arch: arm64 + Log Level: info + Enabled Checks: [rtcontainer container process_discovery] + Allocated Memory: 14,060,976 bytes + Hostname: docker-desktop + System Probe Process Module Status: Not running + Process Language Detection Enabled: False + + ================= + Process Endpoints + ================= + https://process.datadoghq.eu - API Key ending with: + - 72724 + + ========= + Collector + ========= + Last collection time: 2023-11-29 10:42:25 + Docker socket: /var/run/docker.sock + Number of processes: 0 + Number of containers: 1 + Process Queue length: 0 + RTProcess Queue length: 0 + Connections Queue length: 0 + Event Queue length: 0 + Pod Queue length: 0 + Process Bytes enqueued: 0 + RTProcess Bytes enqueued: 0 + Connections Bytes enqueued: 0 + Event Bytes enqueued: 0 + Pod Bytes enqueued: 0 + Drop Check Payloads: [] + + ========== + Extractors + ========== + + Workloadmeta + ============ + Cache size: 0 + Stale diffs discarded: 0 + Diffs dropped: 0 diff --git a/pkg/status/render/fixtures/security_agent_status.json b/pkg/status/render/fixtures/security_agent_status.json new file mode 100644 index 0000000000000..8141515e06686 --- /dev/null +++ b/pkg/status/render/fixtures/security_agent_status.json @@ -0,0 +1,913 @@ +{ + "JMXStartupError": { + "LastError": "", + "Timestamp": 0 + }, + "JMXStatus": { + "info": null, + "checks": { + "initialized_checks": null, + "failed_checks": null + }, + "timestamp": 0, + "errors": 0 + }, + "NoProxyChanged": [], + "NoProxyIgnoredWarningMap": [], + "NoProxyUsedInFuture": [], + "TransportWarnings": false, + "adEnabledFeatures": { + "docker": {} + }, + "agent_metadata": {}, + "agent_start_nano": 1701253052392961884, + "aggregatorStats": { + "ChecksHistogramBucketMetricSample": 0, + "ChecksMetricSample": 0, + "DogstatsdContexts": 0, + "DogstatsdContextsByMtype": { + "Count": 0, + "Counter": 0, + "Distribution": 0, + "Gauge": 0, + "Histogram": 0, + "Historate": 0, + "MonotonicCount": 0, + "Rate": 0, + "Set": 0 + }, + "DogstatsdMetricSample": 1, + "Event": 1, + "EventPlatformEvents": {}, + "EventPlatformEventsErrors": {}, + "EventsFlushErrors": 0, + "EventsFlushed": 1, + "Flush": { + "ChecksMetricSampleFlushTime": { + "FlushIndex": 2, + "Flushes": [ + 16788292, + 1413375, + 7895666, + 3688167, + 16294166, + 1701875, + 1876083, + 3225208, + 496750, + 6249583, + 1440334, + 11195917, + 1185875, + 1397916, + 323500, + 268083, + 5228292, + 3378500, + 12369292, + 8481500, + 4424875, + 225959, + 254208, + 1949792, + 3177625, + 14756875, + 1911209, + 554875, + 299833, + 743208, + 5745083, + 8526000 + ], + "LastFlush": 7895666, + "Name": "ChecksMetricSampleFlushTime" + }, + "EventFlushTime": { + "FlushIndex": 0, + "Flushes": [ + 25612834, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 25612834, + "Name": "EventFlushTime" + }, + "MainFlushTime": { + "FlushIndex": 2, + "Flushes": [ + 16800625, + 1419417, + 7902958, + 3772542, + 16330083, + 1708667, + 1886833, + 3233291, + 500125, + 6261083, + 1451209, + 11205125, + 1190875, + 1404375, + 326208, + 272041, + 5237375, + 3387417, + 12374625, + 8489875, + 4432375, + 228500, + 256291, + 1956625, + 3186084, + 14763833, + 1918084, + 561375, + 302625, + 748875, + 5869291, + 8536917 + ], + "LastFlush": 7902958, + "Name": "MainFlushTime" + }, + "ManifestsTime": { + "FlushIndex": -1, + "Flushes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 0, + "Name": "ManifestsTime" + }, + "MetricSketchFlushTime": { + "FlushIndex": -1, + "Flushes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 0, + "Name": "MetricSketchFlushTime" + }, + "ServiceCheckFlushTime": { + "FlushIndex": 2, + "Flushes": [ + 16755959, + 716125, + 1058750, + 3396125, + 14984125, + 1017458, + 1895166, + 1522583, + 489291, + 5937542, + 1782250, + 12991542, + 1182167, + 660208, + 329416, + 258583, + 9008542, + 721042, + 4216334, + 5173959, + 2696000, + 235709, + 262208, + 1185875, + 723959, + 14414875, + 959334, + 531292, + 314541, + 771208, + 900041, + 3754917 + ], + "LastFlush": 1058750, + "Name": "ServiceCheckFlushTime" + } + }, + "FlushCount": { + "Events": { + "FlushIndex": 0, + "Flushes": [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 1, + "Name": "Events" + }, + "Manifests": { + "FlushIndex": -1, + "Flushes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 0, + "Name": "Manifests" + }, + "Series": { + "FlushIndex": 2, + "Flushes": [ + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2 + ], + "LastFlush": 2, + "Name": "Series" + }, + "ServiceChecks": { + "FlushIndex": 2, + "Flushes": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ], + "LastFlush": 1, + "Name": "ServiceChecks" + }, + "Sketches": { + "FlushIndex": -1, + "Flushes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "LastFlush": 0, + "Name": "Sketches" + } + }, + "HostnameUpdate": 0, + "MetricTags": { + "Series": { + "Above100": 0, + "Above90": 0 + }, + "Sketches": { + "Above100": 0, + "Above90": 0 + } + }, + "NumberOfFlush": 99, + "OrchestratorManifests": 0, + "OrchestratorManifestsErrors": 0, + "OrchestratorMetadata": 0, + "OrchestratorMetadataErrors": 0, + "SeriesFlushErrors": 0, + "SeriesFlushed": 199, + "ServiceCheck": 0, + "ServiceCheckFlushErrors": 0, + "ServiceCheckFlushed": 99, + "SketchesFlushErrors": 0, + "SketchesFlushed": 0 + }, + "autoConfigStats": { + "ConfigErrors": {}, + "ResolveWarnings": {} + }, + "build_arch": "arm64", + "checkSchedulerStats": { + "LoaderErrors": {}, + "RunErrors": {} + }, + "complianceChecks": null, + "conf_file": "/etc/datadog-agent/datadog.yaml", + "config": { + "additional_checksd": "/etc/datadog-agent/checks.d", + "confd_path": "/etc/datadog-agent/conf.d", + "fips_enabled": "false", + "fips_local_address": "localhost", + "fips_port_range_start": "9803", + "log_file": "", + "log_level": "info" + }, + "endpointsInfos": { + "https://app.datadoghq.eu": [ + "72724" + ] + }, + "filterErrors": {}, + "flavor": "security_agent", + "forwarderStats": { + "APIKeyFailure": {}, + "APIKeyStatus": { + "API key ending with 72724": "API Key valid" + }, + "FileStorage": { + "CurrentSizeInBytes": 0, + "DeserializeCount": 0, + "DeserializeErrorsCount": 0, + "DeserializeTransactionsCount": 0, + "FileSize": 0, + "FilesCount": 0, + "FilesRemovedCount": 0, + "PointsDroppedCount": 0, + "SerializeCount": 0, + "StartupReloadedRetryFilesCount": 0 + }, + "RemovalPolicy": { + "FilesFromUnknownDomainCount": 0, + "NewRemovalPolicyCount": 0, + "OutdatedFilesCount": 0, + "RegisteredDomainCount": 0 + }, + "TransactionContainer": { + "CurrentMemSizeInBytes": 0, + "ErrorsCount": 0, + "PointsDroppedCount": 0, + "TransactionsCount": 0, + "TransactionsDroppedCount": 0 + }, + "Transactions": { + "Cluster": 0, + "ClusterRole": 0, + "ClusterRoleBinding": 0, + "ConnectionEvents": { + "ConnectSuccess": 1, + "DNSSuccess": 1 + }, + "CronJob": 0, + "CustomResource": 0, + "CustomResourceDefinition": 0, + "DaemonSet": 0, + "Deployment": 0, + "Dropped": 0, + "DroppedByEndpoint": {}, + "Errors": 0, + "ErrorsByType": { + "ConnectionErrors": 0, + "DNSErrors": 0, + "SentRequestErrors": 0, + "TLSErrors": 0, + "WroteRequestErrors": 0 + }, + "HTTPErrors": 0, + "HTTPErrorsByCode": {}, + "HighPriorityQueueFull": 0, + "HorizontalPodAutoscaler": 0, + "Ingress": 0, + "InputBytesByEndpoint": { + "check_run_v1": 11727, + "intake": 188, + "series_v2": 16457 + }, + "InputCountByEndpoint": { + "check_run_v1": 99, + "intake": 1, + "series_v2": 99 + }, + "Job": 0, + "Namespace": 0, + "Node": 0, + "OrchestratorManifest": 0, + "PersistentVolume": 0, + "PersistentVolumeClaim": 0, + "Pod": 0, + "ReplicaSet": 0, + "Requeued": 0, + "RequeuedByEndpoint": {}, + "Retried": 0, + "RetriedByEndpoint": {}, + "RetryQueueSize": 0, + "Role": 0, + "RoleBinding": 0, + "Service": 0, + "ServiceAccount": 0, + "StatefulSet": 0, + "Success": 199, + "SuccessByEndpoint": { + "check_run_v1": 99, + "connections": 0, + "container": 0, + "events_v2": 0, + "host_metadata_v2": 0, + "intake": 1, + "orchestrator": 0, + "process": 0, + "rtcontainer": 0, + "rtprocess": 0, + "series_v1": 0, + "series_v2": 99, + "services_checks_v2": 0, + "sketches_v1": 0, + "sketches_v2": 0, + "validate_v1": 0 + }, + "SuccessBytesByEndpoint": { + "check_run_v1": 11727, + "intake": 188, + "series_v2": 16457 + }, + "VerticalPodAutoscaler": 0 + } + }, + "go_version": "go1.20.10", + "hostTags": [], + "hostinfo": { + "hostname": "52254030ab59", + "uptime": 2436, + "bootTime": 1701252098, + "procs": 210, + "os": "linux", + "platform": "ubuntu", + "platformFamily": "debian", + "platformVersion": "23.04", + "kernelVersion": "6.4.16-linuxkit", + "kernelArch": "aarch64", + "virtualizationSystem": "docker", + "virtualizationRole": "guest", + "hostId": "1714f7df-2eef-4fcc-9477-476137cc9918" + }, + "hostnameStats": { + "errors": { + "'hostname' configuration/environment": "hostname is empty", + "'hostname_file' configuration/environment": "'hostname_file' configuration is not enabled", + "aws": "not retrieving hostname from AWS: the host is not an ECS instance and other providers already retrieve non-default hostnames", + "azure": "azure_hostname_style is set to 'os'", + "fargate": "agent is not runnning on Fargate", + "fqdn": "FQDN hostname is not usable", + "gce": "unable to retrieve hostname from GCE: GCE metadata API error: Get \"http://169.254.169.254/computeMetadata/v1/instance/hostname\": dial tcp 169.254.169.254:80: connect: connection refused", + "os": "OS hostname is not usable" + }, + "provider": "container" + }, + "inventories": {}, + "logsStats": { + "is_running": false, + "endpoints": null, + "metrics": null, + "process_file_stats": null, + "integrations": null, + "tailers": null, + "errors": null, + "warnings": null, + "use_http": false + }, + "metadata": { + "os": "linux", + "agent-flavor": "security_agent", + "python": "n/a", + "systemStats": { + "cpuCores": 1, + "machine": "arm64", + "platform": "linux", + "pythonV": "n/a", + "processor": "", + "macV": [ + "", + "", + "" + ], + "nixV": [ + "ubuntu", + "23.04", + "" + ], + "fbsdV": [ + "", + "", + "" + ], + "winV": [ + "", + "", + "" + ] + }, + "meta": { + "socket-hostname": "52254030ab59", + "timezones": [ + "UTC" + ], + "socket-fqdn": "52254030ab59", + "ec2-hostname": "", + "hostname": "docker-desktop", + "host_aliases": [], + "instance-id": "" + }, + "host-tags": { + "system": [] + }, + "container-meta": { + "docker_swarm": "inactive", + "docker_version": "24.0.6" + }, + "network": null, + "logs": { + "transport": "", + "auto_multi_line_detection_enabled": false + }, + "install-method": { + "tool": "docker", + "tool_version": "docker", + "installer_version": "docker" + }, + "proxy-info": { + "no-proxy-nonexact-match": false, + "proxy-behavior-changed": false, + "no-proxy-nonexact-match-explicitly-set": false + }, + "otlp": { + "enabled": false + } + }, + "otlp": {}, + "pid": 23865, + "processAgentStatus": { + "core": { + "build_arch": "arm64", + "config": { + "log_level": "info" + }, + "go_version": "go1.20.10", + "metadata": { + "agent-flavor": "process_agent", + "container-meta": { + "docker_swarm": "inactive", + "docker_version": "24.0.6" + }, + "host-tags": { + "system": [] + }, + "install-method": { + "installer_version": "docker", + "tool": "docker", + "tool_version": "docker" + }, + "logs": { + "auto_multi_line_detection_enabled": false, + "transport": "" + }, + "meta": { + "ec2-hostname": "", + "host_aliases": [], + "hostname": "docker-desktop", + "instance-id": "", + "socket-fqdn": "52254030ab59", + "socket-hostname": "52254030ab59", + "timezones": [ + "UTC" + ] + }, + "network": null, + "os": "linux", + "otlp": { + "enabled": false + }, + "proxy-info": { + "no-proxy-nonexact-match": false, + "no-proxy-nonexact-match-explicitly-set": false, + "proxy-behavior-changed": false + }, + "python": "n/a", + "systemStats": { + "cpuCores": 1, + "fbsdV": [ + "", + "", + "" + ], + "macV": [ + "", + "", + "" + ], + "machine": "arm64", + "nixV": [ + "ubuntu", + "23.04", + "" + ], + "platform": "linux", + "processor": "", + "pythonV": "n/a", + "winV": [ + "", + "", + "" + ] + } + }, + "version": "7.49.1" + }, + "date": 1701254547319371000, + "expvars": { + "connections_queue_bytes": 0, + "connections_queue_size": 0, + "container_count": 1, + "container_id": "52254030ab598053a03b9bd474ac11d72486f640def81fc7be38ed3a5e864df3", + "docker_socket": "/var/run/docker.sock", + "drop_check_payloads": [], + "enabled_checks": [ + "rtcontainer", + "container", + "process_discovery" + ], + "endpoints": { + "https://process.datadoghq.eu": [ + "72724" + ] + }, + "event_queue_bytes": 0, + "event_queue_size": 0, + "language_detection_enabled": false, + "last_collect_time": "2023-11-29 10:42:25", + "log_file": "", + "memstats": { + "alloc": 14060976 + }, + "pid": 23863, + "pod_queue_bytes": 0, + "pod_queue_size": 0, + "process_count": 0, + "process_queue_bytes": 0, + "process_queue_size": 0, + "proxy_url": "", + "rtprocess_queue_bytes": 0, + "rtprocess_queue_size": 0, + "system_probe_process_module_enabled": false, + "uptime": 1494, + "uptime_nano": 1701253052422544600, + "version": { + "BuildDate": "", + "GitBranch": "", + "GitCommit": "", + "GoVersion": "", + "Version": "" + }, + "workloadmeta_extractor_cache_size": 0, + "workloadmeta_extractor_diffs_dropped": 0, + "workloadmeta_extractor_stale_diffs": 0 + } + }, + "pyLoaderStats": null, + "pythonInit": null, + "python_version": "n/a", + "remoteConfiguration": {}, + "runnerStats": { + "Checks": {}, + "Running": {}, + "Workers": { + "Count": 0, + "Instances": {} + } + }, + "runtimeSecurityStatus": { + "activityDumpReceived": 0, + "connected": false, + "endpoints": [ + "Reliable: Sending compressed logs in HTTPS to runtime-security-http-intake.logs.datadoghq.eu on port 443" + ], + "eventReceived": 0 + }, + "snmpTrapsStats": { + "metrics": { + "Packets": 0, + "PacketsAuthErrors": 0 + } + }, + "systemProbeStats": { + "Errors": "System Probe is not supported on this system" + }, + "time_nano": 1701254547317708590, + "verbose": false, + "version": "7.49.1" +} diff --git a/pkg/status/render/fixtures/security_agent_status.text b/pkg/status/render/fixtures/security_agent_status.text new file mode 100755 index 0000000000000..e73e0a7ebea6f --- /dev/null +++ b/pkg/status/render/fixtures/security_agent_status.text @@ -0,0 +1,79 @@ + +================================ +Datadog Security Agent (v7.49.1) +================================ + + Status date: 2023-11-29 10:42:27.317 UTC (1701254547317) + Agent start: 2023-11-29 10:17:32.392 UTC (1701253052392) + Pid: 23865 + Go Version: go1.20.10 + Python Version: n/a + Build arch: arm64 + Agent flavor: security_agent + Log Level: info + + Paths + ===== + Config File: /etc/datadog-agent/datadog.yaml + conf.d: /etc/datadog-agent/conf.d + checks.d: /etc/datadog-agent/checks.d + + Clocks + ====== + System time: 2023-11-29 10:42:27.317 UTC (1701254547317) + + Host Info + ========= + bootTime: 2023-11-29 10:01:38 UTC (1701252098000) + hostId: 1714f7df-2eef-4fcc-9477-476137cc9918 + kernelArch: aarch64 + kernelVersion: 6.4.16-linuxkit + os: linux + platform: ubuntu + platformFamily: debian + platformVersion: 23.04 + procs: 210 + uptime: 40m36s + virtualizationRole: guest + virtualizationSystem: docker + + Hostnames + ========= + hostname: docker-desktop + socket-fqdn: 52254030ab59 + socket-hostname: 52254030ab59 + hostname provider: container + unused hostname providers: + 'hostname' configuration/environment: hostname is empty + 'hostname_file' configuration/environment: 'hostname_file' configuration is not enabled + aws: not retrieving hostname from AWS: the host is not an ECS instance and other providers already retrieve non-default hostnames + azure: azure_hostname_style is set to 'os' + fargate: agent is not runnning on Fargate + fqdn: FQDN hostname is not usable + gce: unable to retrieve hostname from GCE: GCE metadata API error: Get "http://169.254.169.254/computeMetadata/v1/instance/hostname": dial tcp 169.254.169.254:80: connect: connection refused + os: OS hostname is not usable + + Metadata + ======== + +================ +Runtime Security +================ + + Reliable: Sending compressed logs in HTTPS to runtime-security-http-intake.logs.datadoghq.eu on port 443 + Connected: false + Events received: 0 + + Self Tests + ========== + + Last execution: + + Succeeded: none + + Failed: none +========== +Compliance +========== + + Not enabled diff --git a/pkg/status/render/render_test.go b/pkg/status/render/render_test.go index 850922e8dece7..1de9262e3ad35 100644 --- a/pkg/status/render/render_test.go +++ b/pkg/status/render/render_test.go @@ -6,27 +6,84 @@ package render import ( + "fmt" "os" + "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestFormatStatus(t *testing.T) { - agentJSON, err := os.ReadFile("fixtures/agent_status.json") - require.NoError(t, err) +func TestFormat(t *testing.T) { + originalTZ := os.Getenv("TZ") + os.Setenv("TZ", "UTC") + defer func() { + os.Setenv("TZ", originalTZ) + }() + const statusRenderErrors = "Status render errors" - t.Run("render errors", func(t *testing.T) { - actual, err := FormatStatus([]byte{}) - require.NoError(t, err) - assert.Contains(t, actual, statusRenderErrors) - }) + tests := []struct { + name string + formatFunction func([]byte) (string, error) + jsonFile string + resultFile string + }{ + { + name: "Core status", + formatFunction: FormatStatus, + jsonFile: "fixtures/agent_status.json", + resultFile: "fixtures/agent_status.text", + }, + { + name: "Cluster Agent Status", + formatFunction: FormatDCAStatus, + jsonFile: "fixtures/cluster_agent_status.json", + resultFile: "fixtures/cluster_agent_status.text", + }, + { + name: "Security Agent Status", + formatFunction: FormatSecurityAgentStatus, + jsonFile: "fixtures/security_agent_status.json", + resultFile: "fixtures/security_agent_status.text", + }, + { + name: "Process Agent Status", + formatFunction: FormatProcessAgentStatus, + jsonFile: "fixtures/process_agent_status.json", + resultFile: "fixtures/process_agent_status.text", + }, + { + name: "Check Stats", + formatFunction: FormatCheckStats, + jsonFile: "fixtures/check_stats.json", + resultFile: "fixtures/check_stats.text", + }, + } - t.Run("no render errors", func(t *testing.T) { - actual, err := FormatStatus(agentJSON) + for _, tt := range tests { + jsonBytes, err := os.ReadFile(tt.jsonFile) require.NoError(t, err) - assert.NotContains(t, actual, statusRenderErrors) - }) + expectedOutput, err := os.ReadFile(tt.resultFile) + require.NoError(t, err) + + t.Run(fmt.Sprintf("%s: render errors", tt.name), func(t *testing.T) { + output, err := tt.formatFunction([]byte{}) + require.NoError(t, err) + assert.Contains(t, output, statusRenderErrors) + }) + + t.Run(fmt.Sprintf("%s: no render errors", tt.name), func(t *testing.T) { + output, err := tt.formatFunction(jsonBytes) + require.NoError(t, err) + + // We replace windows line break by linux so the tests pass on every OS + result := strings.Replace(string(expectedOutput), "\r\n", "\n", -1) + output = strings.Replace(output, "\r\n", "\n", -1) + + assert.Equal(t, output, result) + assert.NotContains(t, output, statusRenderErrors) + }) + } } diff --git a/pkg/status/render/templates/autodiscovery.tmpl b/pkg/status/render/templates/autodiscovery.tmpl index f4f03edbc81b6..3ff340819ac6e 100644 --- a/pkg/status/render/templates/autodiscovery.tmpl +++ b/pkg/status/render/templates/autodiscovery.tmpl @@ -7,7 +7,7 @@ NOTE: Changes made to this template should be reflected on the following templat ============= Autodiscovery ============= -{{- with .adEnabledFeatures -}} +{{- with .adEnabledFeatures}} Enabled Features ================ {{- range $feature, $empty := . }} From 28bf6fdbfcd096c23802cd3813b89abf5614ff6c Mon Sep 17 00:00:00 2001 From: Guillaume Fournier <36961134+Gui774ume@users.noreply.github.com> Date: Thu, 30 Nov 2023 10:58:58 +0100 Subject: [PATCH 87/87] [CWS] reset auto suppression config (#21193) --- pkg/config/system_probe_cws.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/config/system_probe_cws.go b/pkg/config/system_probe_cws.go index 353a8e0749644..c98e08302a9b5 100644 --- a/pkg/config/system_probe_cws.go +++ b/pkg/config/system_probe_cws.go @@ -39,7 +39,7 @@ func initCWSSystemProbeConfig(cfg Config) { cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.load_controller_period", "60s") cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.min_timeout", "10m") cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.max_dump_size", 1750) - cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.traced_cgroups_count", 10) + cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.traced_cgroups_count", 5) cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.traced_event_types", []string{"exec", "open", "dns"}) cfg.BindEnv("runtime_security_config.activity_dump.cgroup_dump_timeout") // deprecated in favor of dump_duration cfg.BindEnvAndSetDefault("runtime_security_config.activity_dump.dump_duration", "900s") @@ -72,12 +72,12 @@ func initCWSSystemProbeConfig(cfg Config) { cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.auto_suppression.enabled", true) // CWS - Anomaly detection - cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.event_types", []string{"exec", "dns", "open"}) + cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.event_types", []string{"exec", "dns"}) cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.default_minimum_stable_period", "900s") cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.minimum_stable_period.exec", "900s") cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.minimum_stable_period.dns", "900s") cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.workload_warmup_period", "180s") - cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.unstable_profile_time_threshold", "120h") + cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.unstable_profile_time_threshold", "1h") cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.unstable_profile_size_threshold", 5000000) cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.rate_limiter.period", "5m") cfg.BindEnvAndSetDefault("runtime_security_config.security_profile.anomaly_detection.rate_limiter.num_keys", 1000)