Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AMLII-933 - Linux log file tailing e2e test #20202

Merged
merged 53 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
c014d79
+ bug fix
DDuongNguyen Aug 28, 2023
a540004
Merge branch 'main' of github.com:DataDog/datadog-agent
DDuongNguyen Aug 28, 2023
2334aad
Merge branch 'main' of github.com:DataDog/datadog-agent
DDuongNguyen Aug 30, 2023
64c6cb1
Merge branch 'main' of github.com:DataDog/datadog-agent
DDuongNguyen Oct 12, 2023
9da23b0
Apply changes from linux-log-agent folder
DDuongNguyen Oct 17, 2023
51135e0
deploy files
DDuongNguyen Oct 17, 2023
93d335e
lint
DDuongNguyen Oct 17, 2023
2063e69
Merge branch 'main' of github.com:DataDog/datadog-agent
DDuongNguyen Oct 19, 2023
9f869f3
adjusted to comments
DDuongNguyen Oct 20, 2023
1f705ac
Merge branch 'main' of github.com:DataDog/datadog-agent
DDuongNguyen Oct 25, 2023
e21eeeb
update test for e2e new changes
DDuongNguyen Oct 31, 2023
16e007f
lint
DDuongNguyen Nov 1, 2023
66b5c1e
Merge branch 'main' of github.com:DataDog/datadog-agent
DDuongNguyen Nov 1, 2023
7862cca
adjusted to comments
DDuongNguyen Nov 2, 2023
91675df
Merge branch 'main' into e2e-linux-log-tailing
DDuongNguyen Nov 2, 2023
399f3c9
Merge branch 'main' of github.com:DataDog/datadog-agent
DDuongNguyen Nov 3, 2023
59a8d8e
Merge branch 'main' of github.com:DataDog/datadog-agent
DDuongNguyen Nov 7, 2023
d14b700
new comments
DDuongNguyen Nov 15, 2023
128c1b3
merged main
DDuongNguyen Nov 15, 2023
20cc38c
Merge branch 'main' into e2e-linux-log-tailing
DDuongNguyen Nov 15, 2023
2f6d298
Merge branch 'main' of github.com:DataDog/datadog-agent
DDuongNguyen Nov 16, 2023
cc0095a
Merge branch 'main' of github.com:DataDog/datadog-agent
DDuongNguyen Nov 16, 2023
52702e1
adjusted to comments
DDuongNguyen Nov 20, 2023
399c938
Merge remote-tracking branch 'origin/e2e-linux-log-tailing' into e2e-…
DDuongNguyen Nov 20, 2023
46cb70e
adjusted
DDuongNguyen Nov 21, 2023
4492f32
lint+license+go mod + comments
DDuongNguyen Nov 21, 2023
87d576e
recommit
DDuongNguyen Nov 21, 2023
4faa457
Merge branch 'main' into e2e-linux-log-tailing
DDuongNguyen Nov 27, 2023
5d81b64
Update test/new-e2e/tests/agent-metric-logs/linux-log-agent/01_file_t…
DDuongNguyen Nov 28, 2023
38dd4b7
Update test/new-e2e/tests/agent-metric-logs/linux-log-agent/01_file_t…
DDuongNguyen Nov 28, 2023
8f1f3b5
lint
DDuongNguyen Nov 28, 2023
2b1aae8
get rid of status check
DDuongNguyen Nov 29, 2023
5621419
sanity checks
DDuongNguyen Dec 4, 2023
6d9964f
comments+lints
DDuongNguyen Dec 4, 2023
16d3070
Merge branch 'main' into e2e-linux-log-tailing
DDuongNguyen Dec 4, 2023
c02e5da
Merge branch 'main' into e2e-linux-log-tailing
DDuongNguyen Dec 4, 2023
6f770cf
lint
DDuongNguyen Dec 4, 2023
957e1f4
Merge branch 'e2e-linux-log-tailing' of github.com:DataDog/datadog-ag…
DDuongNguyen Dec 4, 2023
833f859
make functions helper functions + lints
DDuongNguyen Dec 5, 2023
2ce3d15
Merge branch 'main' into e2e-linux-log-tailing
DDuongNguyen Dec 6, 2023
4074606
new comments
DDuongNguyen Dec 6, 2023
0846fa8
Merge branch 'e2e-linux-log-tailing' of github.com:DataDog/datadog-ag…
DDuongNguyen Dec 6, 2023
7416bd8
lint
DDuongNguyen Dec 6, 2023
b25d5b9
documentation + lint
DDuongNguyen Dec 11, 2023
caf9b62
Merge branch 'main' of github.com:DataDog/datadog-agent
DDuongNguyen Dec 11, 2023
9496578
Merge branch 'main' into e2e-linux-log-tailing
DDuongNguyen Dec 11, 2023
57d3ef4
SSM issue
DDuongNguyen Dec 11, 2023
36b7e50
documentation + break test down into smaller tests
DDuongNguyen Dec 11, 2023
d3ba4d7
[e2e] call base suite cleanup and setup
pducolin Dec 13, 2023
c89fbaf
[e2e] pass devmode option to suite run
pducolin Dec 13, 2023
6427e90
[e2e] embed log config as string
pducolin Dec 13, 2023
76e7371
[e2e] run cleanup after tests
pducolin Dec 13, 2023
b4fd09e
CI name change
DDuongNguyen Dec 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitlab/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ new-e2e-npm-main:
# Temporary, until we manage to stabilize those tests.
allow_failure: true

new-e2e-log-agent-main:
extends: .new_e2e_template
rules: !reference [.on_main]
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved
variables:
TARGETS: ./tests/agent-metric-logs
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved

# ^ If you create a new job here that extends `.new_e2e_template`,
# /!\ do not forget to add it in the `dependencies` statement of the
# /___\ `e2e_test_junit_upload` job in the `.gitlab/e2e_test_junit_upload.yml` file
1 change: 1 addition & 0 deletions .gitlab/e2e_test_junit_upload.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ e2e_test_junit_upload:
- new-e2e-language-detection-main
- new-e2e-agent-platform-install-script-debian-a7-x64
- new-e2e-npm-main
- new-e2e-agent-metric-logs-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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// 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 logagent

import (
"strings"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e"
"github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e/params"
"github.com/DataDog/test-infra-definitions/components/datadog/agentparams"
)

// vmFakeintakeSuite defines a test suite for the log agent interacting with a virtual machine and fake intake.
type vmFakeintakeSuite struct {
e2e.Suite[e2e.FakeIntakeEnv]
}

// logsExampleStackDef returns the stack definition required for the log agent test suite.
func logsExampleStackDef() *e2e.StackDefinition[e2e.FakeIntakeEnv] {
config :=
`logs:
- type: file
path: '/var/log/hello-world.log'
service: hello
source: custom_log
`
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved
return e2e.FakeIntakeStackDef(
e2e.WithAgentParams(
agentparams.WithLogs(),
agentparams.WithIntegration("custom_logs.d", config)))

}

// TestE2EVMFakeintakeSuite runs the E2E test suite for the log agent with a VM and fake intake.
func TestE2EVMFakeintakeSuite(t *testing.T) {
e2e.Run(t, &vmFakeintakeSuite{}, logsExampleStackDef(), params.WithDevMode())
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved
}

func (s *vmFakeintakeSuite) TestLinuxLogTailing() {
// Clean up once test is finished running
s.cleanUp()
defer s.cleanUp()

// Flush server and reset aggregators
s.Env().Fakeintake.FlushServerAndResetAggregators()
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved
defer s.Env().Fakeintake.FlushServerAndResetAggregators()
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved

// Run test cases
s.T().Run("LogCollection", func(t *testing.T) {
s.LogCollection()
})

s.T().Run("LogPermission", func(t *testing.T) {
s.LogPermission()
})

s.T().Run("LogRotation", func(t *testing.T) {
s.LogRotation()
})
L3n41c marked this conversation as resolved.
Show resolved Hide resolved
}

func (s *vmFakeintakeSuite) LogCollection() {
t := s.T()
fakeintake := s.Env().Fakeintake

// Create a new log file
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved
s.Env().VM.Execute("sudo touch /var/log/hello-world.log")

// Part 1: Ensure no logs are present in fakeintake
s.EventuallyWithT(func(c *assert.CollectT) {
logs, err := fakeintake.FilterLogs("hello")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This filters for any logs from the service 'hello'. Should we simplify this and just assert that we don't find the log we emit below?
My thought is that by being more specific we would reduce any chances of false-positives.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So currently we do have different log content for different scenarios. Mainly:
hello-world
access-denied
access-granted
hello-world-new-content

We could check for all those 4 specific logs but i feel like that is less efficient vs just checking for the intake for all those logs sent via the same service tag. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that is the case, then this assertion should be part of the parent suite as a precondition to running the tests.

require.NoError(t, err, "Unable to filter logs by the service 'hello'.")
require.Empty(t, logs, "Logs were found when none were expected.")
L3n41c marked this conversation as resolved.
Show resolved Hide resolved

// If logs are found, print their content for debugging
if len(logs) != 0 {
cat, _ := s.Env().VM.ExecuteWithError("cat /var/log/hello-world.log")
t.Logf("Logs detected when none were expected: %v", cat)
require.Empty(t, logs, "Logs were found when none were expected.")
}
}, 5*time.Minute, 10*time.Second)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This EventuallyWithT is asserting that there are no logs present already (before the logs were accessible to the agent), correct?
If that's the case, shouldn't that be a very short (maybe even synchronous) check? Do these last two arguments mean that it will retry every 10s for 5min to assert that the logs are continuously empty?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a pending feature in Eventually that is early returning on error on require. Current behaviour is a panic that stops the test. Once that is implemented, we can change this function to retry on fakeintake.FilterLogs("hello") and not on assert.Empty.

This code retries on failures on assert.NoError and on assert.Empty. This means if will keep on failing if logs are not empty, as we don't flush them - and we don't want to

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, got it, thanks for the explanation, I had misunderstood when it retries.

This makes sense to me then, in the normal case this should run once, find that neither of the asserts fails, and proceed with the rest of the test.


// Part 2: Adjust permissions of new log file
_, err := s.Env().VM.ExecuteWithError("sudo chmod 777 /var/log/hello-world.log")
require.NoError(t, err, "Unable to adjust permissions for the log file '/var/log/hello-world.log'.")

// Generate log
generateLog(s, t, "hello-world")
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved

// Part 3: Assert that logs are found in intake after generation
checkLogs(s, "hello", "hello-world")
}

func (s *vmFakeintakeSuite) LogPermission() {
t := s.T()

// Part 4: Block permission and check the Agent status
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we not get a fresh filesystem in this test? If this is relying on /var/log/hello-world.log to exist from the previous test, we should make that dependency explicit in some way. Maybe we assert on the files existence?

Right now if this is just relying on the ordering of the cases in TestLinuxLogTailing, that is very subtle and feels easy to break in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we technically check for the existence of the log file via error from the chmod and generate log since both will generate errors if the log file does not exist. I can put a sanity check at the beginning of the function to see if the log file exist

s.Env().VM.Execute("sudo chmod 000 /var/log/hello-world.log")
s.EventuallyWithT(func(c *assert.CollectT) {
// Check the Agent status
statusOutput, err := s.Env().VM.ExecuteWithError("sudo datadog-agent status | grep -A 10 'custom_logs'")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 suggestion
You can also use the Status command from the s.Env().Agent client

require.NoError(t, err, "Issue running agent status: %s", err)

if strings.Contains(statusOutput, "Status: OK") {
require.Fail(t, "log file is unexpectedly accessible")
}

require.Contains(t, statusOutput, "denied", "Log file is correctly inaccessible")
}, 3*time.Minute, 10*time.Second)

// Part 5: Restore permissions
s.Env().VM.Execute("sudo chmod 777 /var/log/hello-world.log")

// Part 6: Restart the agent, generate new logs
s.Env().VM.Execute("sudo service datadog-agent restart")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ question
Why do you need to restart the agent ? Does the agent stops checking when a file is unaccessible ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be way way back when the agent was in 7.10x or something but that used to be the behavior where users have to manually restart the agent when the permission changes 😅 . I just did a quick test and it looks like the agent now will rescan just fine when the permission changes so I'll get rid of this line

Copy link
Contributor Author

@DDuongNguyen DDuongNguyen Nov 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be a flaky issue, but it looks like even though the agent should rescan, the test fails to get new logs unless the agent is restarted. I can bring it up to OH next week

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, is this an expected feature ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this you could add a call to wait for the agent to be ready, there should be an helper on the agent client

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to add a blocking call to wait for the agent to be ready as @pducolin suggests?


generateLog(s, s.T(), "hello-world")

// Check the Agent status
s.EventuallyWithT(func(c *assert.CollectT) {
statusOutput, err := s.Env().VM.ExecuteWithError("sudo datadog-agent status | grep -A 10 'custom_logs'")
require.NoError(t, err, "Issue running agent status: %s", err)
require.Contains(t, statusOutput, "Status: OK", "Expecting log file to be accessible but it is inaccessible instead")
}, 5*time.Minute, 2*time.Second)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should have a Part 7 that checks if the log actually made it to fake-intake, shouldn't we?

}

func (s *vmFakeintakeSuite) LogRotation() {
t := s.T()

// Part 7: Rotate the log file and check if the agent is tailing the new log file.
// Rotate the log file
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved
s.Env().VM.Execute("sudo mv /var/log/hello-world.log /var/log/hello-world.log.old && sudo touch /var/log/hello-world.log")

// Verify the old log file's existence after rotation
_, err := s.Env().VM.ExecuteWithError("ls /var/log/hello-world.log.old")
require.NoError(t, err, "Failed to find the old log file after rotation")

// Grant new log file permission
s.Env().VM.Execute("sudo chmod 777 /var/log/hello-world.log")

// Check if agent is tailing new log file via agent status
s.EventuallyWithT(func(c *assert.CollectT) {
newStatusOutput, err := s.Env().VM.ExecuteWithError("sudo datadog-agent status | grep -A 10 'custom_logs'")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 suggestion
You could use the stack definition with VM, fakeintake and agent client, to help running status command - the gain is tiny

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any built-in support to query the status object before its turned into template text?
Ie, instead of using agent status to query, query the status HTTP endpoint directly and expose the returned JSON?

I'm hesitant to rely too much on agent status for tests in general, but it would be less error-prone if we're querying and asserting on a specific json field vs assert.Contains

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could move this implementation by @KevinFairise2 to common helpers, it executes the status command on the agent client (s.Env().Agent) and parses it using json library

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed you can skip status, except if you want to test status, and check fakeintake directly

require.NoErrorf(t, err, "Issue running agent status. Is the agent running?\n %s", newStatusOutput)
assert.Containsf(t, newStatusOutput, "Path: /var/log/hello-world.log", "The agent is not tailing the expected log file,instead: \n %s", newStatusOutput)
}, 5*time.Minute, 10*time.Second)

// Generate new log
generateLog(s, t, "hello-world-new-content")

// Verify Log's content is generated and submitted
checkLogs(s, "hello", "hello-world-new-content")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// 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 logagent

import (
"errors"
"fmt"
"strings"
"testing"
"time"

fi "github.com/DataDog/datadog-agent/test/fakeintake/client"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// generateLog generates and verifies log contents.
func generateLog(s *vmFakeintakeSuite, t *testing.T, content string) {
// Determine the OS and set the appropriate log path and command.
var logPath, cmd, checkCmd string
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved

osType, err := s.getOSType()
require.NoErrorf(t, err, "Failed to get OS type: %s", err)

switch osType {
carlosroman marked this conversation as resolved.
Show resolved Hide resolved
case "windows":
t.Log("Generating Windows log.")
logPath = "C:\\logs\\hello-world.log"
cmd = fmt.Sprintf("echo %s > %s", strings.Repeat(content, 10), logPath)
checkCmd = fmt.Sprintf("Get-Content %s", logPath)
default: // Assuming Linux if not Windows.
t.Log("Generating Linux log.")
logPath = "/var/log/hello-world.log"
cmd = fmt.Sprintf("echo %s > %s", strings.Repeat(content, 10), logPath)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we repeat the 'content' 10 times? This is slightly surprising to me as a user of generateLog, I would expect generateLog to write exactly the log that I pass as an argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking of in the future if we have tests that require to send big payload. So for now I'll just pass in a parameter that allow users to repeat contents X amount of time?

checkCmd = fmt.Sprintf("cat %s", logPath)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏 praise
Nice !


s.Env().VM.Execute(cmd)

// Check if the log has been generated.
s.EventuallyWithT(func(c *assert.CollectT) {
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved
output := s.Env().VM.Execute(checkCmd)
if strings.Contains(output, content) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check will give incorrectly pass if you ask for the same content multiple times. The first content addition may succeed, and the second one may fail, but if the second one fails, this check will pass because it was added in the first call to generateLogs.

This is also not a blocking comment for this review, just pointing this out for future improvement.

t.Logf("Finished generating %s log.", osType)
} else {
require.Fail(t, "Log not yet generated.")
}
}, 5*time.Minute, 2*time.Second)
}

// checkLogs checks and verifies logs inside the intake.
func checkLogs(fakeintake *vmFakeintakeSuite, service, content string) {
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved
client := fakeintake.Env().Fakeintake
t := fakeintake.T()

fakeintake.EventuallyWithT(func(c *assert.CollectT) {
names, err := client.GetLogServiceNames()
assert.NoErrorf(t, err, "Error found: %s", err)

if len(names) > 0 {
logs, err := client.FilterLogs(service)
assert.NoErrorf(t, err, "Error found: %s", err)
assert.NotEmpty(t, logs, "No logs with service matching '%s' found, instead got '%s'", service, names)

logs, err = client.FilterLogs(service, fi.WithMessageContaining(content))
assert.NoErrorf(t, err, "Error found: %s", err)
assert.True(t, len(logs) > 0, "Expected at least 1 log with content: '%s', but received %v logs.", content, len(logs))
}
}, 10*time.Minute, 10*time.Second)

}

func (s *vmFakeintakeSuite) getOSType() (string, error) {
// Get Linux OS.
output, err := s.Env().VM.ExecuteWithError("cat /etc/os-release")
if err == nil && strings.Contains(output, "ID=ubuntu") {
return "linux", nil
}

// Get Windows OS.
output, err = s.Env().VM.ExecuteWithError("wmic os get Caption")
if err == nil && strings.Contains(output, "Windows") {
return "windows", nil
}

return "", errors.New("unable to determine OS type.")
}
DDuongNguyen marked this conversation as resolved.
Show resolved Hide resolved

// cleanUp cleans up any existing log files.
func (s *vmFakeintakeSuite) cleanUp() {
t := s.T()
osType, err := s.getOSType()
if err != nil {
t.Logf("Failed to determine OS type: %v", err)
return
}

var checkCmd string

switch osType {
case "linux":
s.Env().VM.Execute("sudo rm -f /var/log/hello-world.log")
s.Env().VM.Execute("sudo rm -f /var/log/hello-world.log.old")
checkCmd = "ls /var/log/hello-world.log /var/log/hello-world.log.old 2>/dev/null || echo 'Files do not exist'"
case "windows":
s.Env().VM.Execute("if (Test-Path C:\\logs\\hello-world.log) { Remove-Item -Path C:\\logs\\hello-world.log -Force }")
s.Env().VM.Execute("if (Test-Path C:\\logs\\hello-world.log.old) { Remove-Item -Path C:\\logs\\hello-world.log.old -Force }")
checkCmd = "if (Test-Path C:\\logs\\hello-world.log) { Get-ChildItem -Path C:\\logs\\hello-world.log } elseif (Test-Path C:\\logs\\hello-world.log.old) { Get-ChildItem -Path C:\\logs\\hello-world.log.old } else { Write-Output 'Files do not exist' }"
default:
t.Logf("Unsupported OS type: %s", osType)
return
}

s.EventuallyWithT(func(c *assert.CollectT) {
output, err := s.Env().VM.ExecuteWithError(checkCmd)
if err != nil {
require.NoErrorf(t, err, "Having issue cleaning log files, retrying... %s", output)
} else {
t.Log("Successfully cleaned up.")
}
}, 5*time.Minute, 2*time.Second)
}