From 54d802482e325bdf17bf635c035214a16d8d01e1 Mon Sep 17 00:00:00 2001 From: Johannes Edmeier Date: Thu, 13 Mar 2025 17:44:44 +0100 Subject: [PATCH 1/9] build: don't use kvm for e2e tests --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9221277e..55921667 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,6 @@ jobs: with: go_version: '1.24' runs_on: steadybit_runner_ubuntu_latest_4cores_16GB - use_kvm: true run_make_prepare_audit: true build_linux_packages: true VERSION_BUMPER_APPID: ${{ vars.GH_APP_STEADYBIT_APP_ID }} From c54bf6fd6cc45962bb7134461254f401cd6b1eab Mon Sep 17 00:00:00 2001 From: Johannes Edmeier Date: Fri, 31 Oct 2025 15:28:39 +0100 Subject: [PATCH 2/9] test: latency is doubled due to the tunnel --- e2e/integration_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/e2e/integration_test.go b/e2e/integration_test.go index 2025e396..1155289a 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -1,10 +1,10 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2025 Steadybit GmbH + /* * Copyright 2024 steadybit GmbH. All rights reserved. */ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2023 Steadybit GmbH - package e2e import ( @@ -637,7 +637,7 @@ func testNetworkDelayTcpPsh(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { require.NoError(t, err) if tt.wantedDelay { - nginx.AssertHttpLatency(t, unaffectedLatency+time.Duration(config.Delay*2)*time.Millisecond*90/100, unaffectedLatency+time.Duration(config.Delay*2)*time.Millisecond*110/100) + nginx.AssertHttpLatency(t, unaffectedLatency+time.Duration(config.Delay*2)*time.Millisecond*90/100, unaffectedLatency+time.Duration(config.Delay)*time.Millisecond*200/100) } else { nginx.AssertHttpLatency(t, 0, unaffectedLatency+40*time.Millisecond) } From 58f24de13fdcac5176c980f324415dcaf8f3e341 Mon Sep 17 00:00:00 2001 From: Johannes Edmeier Date: Fri, 31 Oct 2025 16:08:53 +0100 Subject: [PATCH 3/9] test: latency is doubled due to the tunnel --- e2e/integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/integration_test.go b/e2e/integration_test.go index 1155289a..2dceb977 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -637,7 +637,7 @@ func testNetworkDelayTcpPsh(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { require.NoError(t, err) if tt.wantedDelay { - nginx.AssertHttpLatency(t, unaffectedLatency+time.Duration(config.Delay*2)*time.Millisecond*90/100, unaffectedLatency+time.Duration(config.Delay)*time.Millisecond*200/100) + nginx.AssertHttpLatency(t, unaffectedLatency+time.Duration(config.Delay)*time.Millisecond*90/100, unaffectedLatency+time.Duration(config.Delay*3)*time.Millisecond*110/100) } else { nginx.AssertHttpLatency(t, 0, unaffectedLatency+40*time.Millisecond) } From 8a3874c710b72e2e7ca4b556fd8666e8ee694520 Mon Sep 17 00:00:00 2001 From: Johannes Edmeier Date: Fri, 31 Oct 2025 18:49:31 +0100 Subject: [PATCH 4/9] test: prettier assertFileHasSize --- e2e/integration_test.go | 104 ++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/e2e/integration_test.go b/e2e/integration_test.go index 2dceb977..ac333ec4 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -11,7 +11,6 @@ import ( "bytes" "context" "fmt" - "math" "net" "os/exec" "strconv" @@ -965,8 +964,8 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { size int blockSize int method diskfill.Method - wantedFileSize func(m *e2e.Minikube) int - wantedDelta int + wantedFileSize func(m *e2e.Minikube) int64 + allowedDelta int64 wantedLog *string } testCases := []testCase{ @@ -976,11 +975,11 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { size: 90, blockSize: 0, method: diskfill.AtOnce, - wantedFileSize: func(m *e2e.Minikube) int { + wantedFileSize: func(m *e2e.Minikube) int64 { diskSpace := getDiskSpace(m) - return int(((diskSpace.Capacity * 90 / 100) - diskSpace.Used) / 1024) + return ((diskSpace.Capacity * 90 / 100) - diskSpace.Used) / 1024 }, - wantedDelta: 512, + allowedDelta: 512, }, { name: "fill disk with megabytes to fill (fallocate)", @@ -988,10 +987,10 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { size: 4 * 1024, // 4GB blockSize: 0, method: diskfill.AtOnce, - wantedFileSize: func(_ *e2e.Minikube) int { + wantedFileSize: func(_ *e2e.Minikube) int64 { return 4 * 1024 }, - wantedDelta: 0, + allowedDelta: 0, }, { name: "fill disk with megabytes left (fallocate)", @@ -999,11 +998,11 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { size: 4 * 1024, // 4GB blockSize: 0, method: diskfill.AtOnce, - wantedFileSize: func(m *e2e.Minikube) int { + wantedFileSize: func(m *e2e.Minikube) int64 { diskSpace := getDiskSpace(m) - return int(diskSpace.Available-(int64(4*1024*1024))) / 1024 + return int64(diskSpace.Available-(int64(4*1024*1024))) / 1024 }, - wantedDelta: 512, + allowedDelta: 512, }, { name: "fill disk with percentage (dd)", @@ -1011,11 +1010,11 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { size: 90, blockSize: 5, method: diskfill.OverTime, - wantedFileSize: func(m *e2e.Minikube) int { + wantedFileSize: func(m *e2e.Minikube) int64 { diskSpace := getDiskSpace(m) - return int(((diskSpace.Capacity * 90 / 100) - diskSpace.Used) / 1024) + return ((diskSpace.Capacity * 90 / 100) - diskSpace.Used) / 1024 }, - wantedDelta: 512, + allowedDelta: 512, }, { name: "fill disk with megabytes to fill (dd)", @@ -1023,10 +1022,10 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { size: 4 * 1024, // 4GB blockSize: 1, method: diskfill.OverTime, - wantedFileSize: func(_ *e2e.Minikube) int { + wantedFileSize: func(_ *e2e.Minikube) int64 { return 4 * 1024 }, - wantedDelta: 0, + allowedDelta: 0, }, { name: "fill disk with megabytes left (dd)", @@ -1034,11 +1033,11 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { size: 1 * 1024, blockSize: 5, method: diskfill.OverTime, - wantedFileSize: func(m *e2e.Minikube) int { + wantedFileSize: func(m *e2e.Minikube) int64 { diskSpace := getDiskSpace(m) - return int(diskSpace.Available-(int64(1*1024*1024))) / 1024 + return int64(diskSpace.Available-(int64(1*1024*1024))) / 1024 }, - wantedDelta: 512, + allowedDelta: 512, }, { name: "fill disk with bigger blocksize (dd)", @@ -1046,10 +1045,10 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { size: 4 * 1024, // 4GB blockSize: 6 * 1024, // 2GB method: diskfill.OverTime, - wantedFileSize: func(_ *e2e.Minikube) int { + wantedFileSize: func(_ *e2e.Minikube) int64 { return 4 * 1024 // 4GB }, - wantedDelta: 512, + allowedDelta: 512, }, { name: "fill disk with noop because disk is already full (fallocate)", @@ -1057,16 +1056,16 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { size: 5, blockSize: 5, method: diskfill.AtOnce, - wantedFileSize: func(_ *e2e.Minikube) int { + wantedFileSize: func(_ *e2e.Minikube) int64 { return 4 * 1024 // 4GB }, - wantedDelta: -1, - wantedLog: extutil.Ptr("disk is already filled up to"), + allowedDelta: -1, + wantedLog: extutil.Ptr("disk is already filled up to"), }, } - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { config := struct { Duration int `json:"duration"` Path string `json:"path"` @@ -1074,27 +1073,27 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { Mode string `json:"mode"` BlockSize int `json:"blocksize"` Method string `json:"method"` - }{Duration: 60_000, Size: testCase.size, Mode: string(testCase.mode), Method: string(testCase.method), BlockSize: testCase.blockSize, Path: pathToFill} - wantedFileSize := testCase.wantedFileSize(m) + }{Duration: 60_000, Size: tt.size, Mode: string(tt.mode), Method: string(tt.method), BlockSize: tt.blockSize, Path: pathToFill} + wantedFileSize := tt.wantedFileSize(m) action, err := e.RunAction(fmt.Sprintf("%s.fill_disk", exthost.BaseActionID), getTarget(m), config, defaultExecutionContext) defer func() { _ = action.Cancel() }() require.NoError(t, err) - if testCase.method == diskfill.OverTime { + if tt.method == diskfill.OverTime { e2e.AssertProcessRunningInContainer(t, m, e.Pod, "extension", "dd", true) } - if testCase.wantedDelta != -1 { - assertFileHasSize(t, m, "/filldisk/disk-fill", wantedFileSize, testCase.wantedDelta) + if tt.allowedDelta != -1 { + assertFileHasSize(t, m, "/filldisk/disk-fill", wantedFileSize, tt.allowedDelta) } - if testCase.wantedLog != nil { - e2e.AssertLogContains(t, m, e.Pod, *testCase.wantedLog) + if tt.wantedLog != nil { + e2e.AssertLogContains(t, m, e.Pod, *tt.wantedLog) } require.NoError(t, action.Cancel()) - if testCase.method == diskfill.OverTime { + if tt.method == diskfill.OverTime { e2e.AssertProcessNOTRunningInContainer(t, m, e.Pod, "extension", "dd") } else { e2e.AssertProcessNOTRunningInContainer(t, m, e.Pod, "extension", "fallocate") @@ -1136,9 +1135,10 @@ func testStressCombined(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { requireAllSidecarsCleanedUp(t, m, e) } -func assertFileHasSize(t *testing.T, m *e2e.Minikube, filepath string, wantedSizeInMb int, wantedDeltaInMb int) { +func assertFileHasSize(t *testing.T, m *e2e.Minikube, filepath string, wantedSizeInMb int64, allowedDeltaInMb int64) { + t.Helper() sizeInBytes := wantedSizeInMb * 1024 * 1024 - deltaInBytes := wantedDeltaInMb * 1024 * 1024 + allowedDeltaInBytes := allowedDeltaInMb * 1024 * 1024 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() message := "" @@ -1154,12 +1154,17 @@ func assertFileHasSize(t *testing.T, m *e2e.Minikube, filepath string, wantedSiz message = fmt.Sprintf("%s: %s", err.Error(), out) continue } - if fileSize, err := strconv.Atoi(strings.TrimSpace(string(out))); err == nil { - actualDelta := int(math.Abs(float64(fileSize - sizeInBytes))) - if actualDelta <= deltaInBytes { + if fileSize, err := strconv.ParseInt(strings.TrimSpace(string(out)), 10, 64); err == nil { + var actualDelta int64 + if fileSize < sizeInBytes { + actualDelta = sizeInBytes - fileSize + } else { + actualDelta = fileSize - sizeInBytes + } + if actualDelta <= allowedDeltaInBytes { return } else { - message = fmt.Sprintf("file size is %d, wanted %d, delta of %d exceeds allowed delta of %d", fileSize, sizeInBytes, actualDelta, deltaInBytes) + message = fmt.Sprintf("file size is %s, wanted %s, delta of %s exceeds allowed delta of %s", prettyBytes(fileSize), prettyBytes(sizeInBytes), prettyBytes(actualDelta), prettyBytes(allowedDeltaInBytes)) } } else { message = fmt.Sprintf("cannot parse file size: %s", err.Error()) @@ -1168,6 +1173,25 @@ func assertFileHasSize(t *testing.T, m *e2e.Minikube, filepath string, wantedSiz } } +func prettyBytes(b int64) string { + const unit = 1024 + sign := "" + if b < 0 { + b = -b + sign = "-" + } + + if b < unit { + return fmt.Sprintf("%d B", b) + } + div, exp := int64(unit), 0 + for n := b / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%s%.1f %ciB", sign, float64(b)/float64(div), "KMGTPE"[exp]) +} + func testNetworkDelayAndBandwidthOnSameContainer(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { configDelay := struct { Duration int `json:"duration"` From 6507d4136b58f86ead06f93ac550abcaf9892c88 Mon Sep 17 00:00:00 2001 From: Johannes Edmeier Date: Fri, 31 Oct 2025 22:55:02 +0100 Subject: [PATCH 5/9] test: fix fill_disk tests on non-kvm --- e2e/integration_test.go | 166 +++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 89 deletions(-) diff --git a/e2e/integration_test.go b/e2e/integration_test.go index ac333ec4..2ad407d1 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -13,6 +13,7 @@ import ( "fmt" "net" "os/exec" + "path" "strconv" "strings" "testing" @@ -947,16 +948,11 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { err := m.SshExec("sudo", "mkdir", "-p", pathToFill).Run() require.NoError(t, err) - var getDiskSpace = func(m *e2e.Minikube) diskfill.DiskUsage { - dfOutput, err := runInMinikube(m, "df", "--sync", "-k", "--output=source,target,fstype,file,size,avail,used", pathToFill) - require.NoError(t, err) - - diskSpace, err := diskfill.CalculateDiskUsage(bytes.NewReader(dfOutput)) - require.NoError(t, err) + diskSpace, err := getDiskSpace(m, pathToFill) + require.NoError(t, err) - log.Debug().Msgf("Disk usage on Host: %+v", diskSpace) - return diskSpace - } + twoGbExtraAsLeft := (diskSpace.Available - (2 * 1024 * 1024)) / 1024 + twoGbExtraAsPercent := (((2 * 1024 * 1024) + diskSpace.Used) * 100 / diskSpace.Capacity) type testCase struct { name string @@ -964,103 +960,84 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { size int blockSize int method diskfill.Method - wantedFileSize func(m *e2e.Minikube) int64 + wantedFileSize int64 allowedDelta int64 wantedLog *string } testCases := []testCase{ { - name: "fill disk with percentage (fallocate)", - mode: diskfill.Percentage, - size: 90, - blockSize: 0, - method: diskfill.AtOnce, - wantedFileSize: func(m *e2e.Minikube) int64 { - diskSpace := getDiskSpace(m) - return ((diskSpace.Capacity * 90 / 100) - diskSpace.Used) / 1024 - }, - allowedDelta: 512, + name: "fill disk with percentage (fallocate)", + mode: diskfill.Percentage, + size: int(twoGbExtraAsPercent), + blockSize: 0, + method: diskfill.AtOnce, + wantedFileSize: 2 * 1024, + allowedDelta: 512, }, { - name: "fill disk with megabytes to fill (fallocate)", - mode: diskfill.MBToFill, - size: 4 * 1024, // 4GB - blockSize: 0, - method: diskfill.AtOnce, - wantedFileSize: func(_ *e2e.Minikube) int64 { - return 4 * 1024 - }, - allowedDelta: 0, + name: "fill disk with megabytes to fill (fallocate)", + mode: diskfill.MBToFill, + size: 4 * 1024, // 4GB + blockSize: 0, + method: diskfill.AtOnce, + wantedFileSize: 4 * 1024, + allowedDelta: 0, }, { - name: "fill disk with megabytes left (fallocate)", - mode: diskfill.MBLeft, - size: 4 * 1024, // 4GB - blockSize: 0, - method: diskfill.AtOnce, - wantedFileSize: func(m *e2e.Minikube) int64 { - diskSpace := getDiskSpace(m) - return int64(diskSpace.Available-(int64(4*1024*1024))) / 1024 - }, - allowedDelta: 512, + name: "fill disk with megabytes left (fallocate)", + mode: diskfill.MBLeft, + size: int(twoGbExtraAsLeft), // 4GB + blockSize: 0, + method: diskfill.AtOnce, + wantedFileSize: 2 * 1024, + allowedDelta: 512, }, { - name: "fill disk with percentage (dd)", - mode: diskfill.Percentage, - size: 90, - blockSize: 5, - method: diskfill.OverTime, - wantedFileSize: func(m *e2e.Minikube) int64 { - diskSpace := getDiskSpace(m) - return ((diskSpace.Capacity * 90 / 100) - diskSpace.Used) / 1024 - }, + name: "fill disk with percentage (dd)", + mode: diskfill.Percentage, + size: int(twoGbExtraAsPercent), + blockSize: 5, + method: diskfill.OverTime, + wantedFileSize: 2 * 1024, + allowedDelta: 512, }, { - name: "fill disk with megabytes to fill (dd)", - mode: diskfill.MBToFill, - size: 4 * 1024, // 4GB - blockSize: 1, - method: diskfill.OverTime, - wantedFileSize: func(_ *e2e.Minikube) int64 { - return 4 * 1024 - }, - allowedDelta: 0, + name: "fill disk with megabytes to fill (dd)", + mode: diskfill.MBToFill, + size: 4 * 1024, // 4GB + blockSize: 1, + method: diskfill.OverTime, + wantedFileSize: 4 * 1024, + allowedDelta: 0, }, { - name: "fill disk with megabytes left (dd)", - mode: diskfill.MBLeft, - size: 1 * 1024, - blockSize: 5, - method: diskfill.OverTime, - wantedFileSize: func(m *e2e.Minikube) int64 { - diskSpace := getDiskSpace(m) - return int64(diskSpace.Available-(int64(1*1024*1024))) / 1024 - }, - allowedDelta: 512, + name: "fill disk with megabytes left (dd)", + mode: diskfill.MBLeft, + size: int(twoGbExtraAsLeft), + blockSize: 5, + method: diskfill.OverTime, + wantedFileSize: 2 * 1024, + allowedDelta: 512, }, { - name: "fill disk with bigger blocksize (dd)", - mode: diskfill.MBToFill, - size: 4 * 1024, // 4GB - blockSize: 6 * 1024, // 2GB - method: diskfill.OverTime, - wantedFileSize: func(_ *e2e.Minikube) int64 { - return 4 * 1024 // 4GB - }, - allowedDelta: 512, + name: "fill disk with bigger blocksize (dd)", + mode: diskfill.MBToFill, + size: 4 * 1024, // 4GB + blockSize: 6 * 1024, // 2GB + method: diskfill.OverTime, + wantedFileSize: 4 * 1024, + allowedDelta: 512, }, { - name: "fill disk with noop because disk is already full (fallocate)", - mode: diskfill.Percentage, - size: 5, - blockSize: 5, - method: diskfill.AtOnce, - wantedFileSize: func(_ *e2e.Minikube) int64 { - return 4 * 1024 // 4GB - }, - allowedDelta: -1, - wantedLog: extutil.Ptr("disk is already filled up to"), + name: "fill disk with noop because disk is already full (fallocate)", + mode: diskfill.Percentage, + size: 5, + blockSize: 5, + method: diskfill.AtOnce, + wantedFileSize: 4 * 1024, + allowedDelta: -1, + wantedLog: extutil.Ptr("disk is already filled up to"), }, } @@ -1074,9 +1051,11 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { BlockSize int `json:"blocksize"` Method string `json:"method"` }{Duration: 60_000, Size: tt.size, Mode: string(tt.mode), Method: string(tt.method), BlockSize: tt.blockSize, Path: pathToFill} - wantedFileSize := tt.wantedFileSize(m) action, err := e.RunAction(fmt.Sprintf("%s.fill_disk", exthost.BaseActionID), getTarget(m), config, defaultExecutionContext) - defer func() { _ = action.Cancel() }() + defer func() { + _ = action.Cancel() + _ = m.SshExec("sudo", "rm", "-f", path.Join(pathToFill, "*")).Run() + }() require.NoError(t, err) if tt.method == diskfill.OverTime { @@ -1084,7 +1063,7 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { } if tt.allowedDelta != -1 { - assertFileHasSize(t, m, "/filldisk/disk-fill", wantedFileSize, tt.allowedDelta) + assertFileHasSize(t, m, "/filldisk/disk-fill", tt.wantedFileSize, tt.allowedDelta) } if tt.wantedLog != nil { @@ -1106,6 +1085,15 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { requireAllSidecarsCleanedUp(t, m, e) } +func getDiskSpace(m *e2e.Minikube, pathToFill string) (diskfill.DiskUsage, error) { + dfOutput, err := runInMinikube(m, "df", "--sync", "-k", "--output=source,target,fstype,file,size,avail,used", pathToFill) + if err != nil { + return diskfill.DiskUsage{}, err + } + + return diskfill.CalculateDiskUsage(bytes.NewReader(dfOutput)) +} + func testStressCombined(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { memConfig := struct { Duration int `json:"duration"` From ce643ce53df7b1552bab7e36ae2c252b77a1eda4 Mon Sep 17 00:00:00 2001 From: Johannes Edmeier Date: Fri, 31 Oct 2025 23:28:39 +0100 Subject: [PATCH 6/9] test: fix fill_disk tests on non-kvm --- e2e/integration_test.go | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/e2e/integration_test.go b/e2e/integration_test.go index 2ad407d1..fe49b4c1 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -280,24 +280,29 @@ func testTimeTravel(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { DisableNtp: true, } + containerTime := getContainerTime(t, m, e) offsetContainer := time.Until(getContainerTime(t, m, e)) + fmt.Printf("before containerTime %s offsetContainer %s\n", containerTime, offsetContainer) action, err := e.RunAction(exthost.BaseActionID+".timetravel", getTarget(m), config, nil) defer func() { _ = action.Cancel() }() require.NoError(t, err) assert.EventuallyWithT(t, func(c *assert.CollectT) { - adjustedContainerTime := getContainerTime(t, m, e).Add(offsetContainer) - diff := time.Until(adjustedContainerTime) - assert.InDelta(t, config.Offset, diff.Milliseconds(), 2000) + containerTime := getContainerTime(t, m, e) + diff := time.Until(containerTime) + adjustedDiff := diff + offsetContainer + assert.InDelta(t, config.Offset, adjustedDiff.Milliseconds(), 2000) + fmt.Printf("during containerTime %s offsetContainer %s diff %s adjustedDiff %s\n", containerTime, offsetContainer, diff, adjustedDiff) }, 10*time.Second, 1*time.Second, "time travel failed to apply offset") // rollback require.NoError(t, action.Cancel()) assert.EventuallyWithT(t, func(c *assert.CollectT) { - adjustedContainerTime := getContainerTime(t, m, e).Add(offsetContainer) - diff := time.Until(adjustedContainerTime) - assert.InDelta(t, 0, diff.Milliseconds(), 2000) + containerTime := getContainerTime(t, m, e) + diff := time.Until(containerTime) + adjustedDiff := diff + offsetContainer + assert.InDelta(t, 0, adjustedDiff.Milliseconds(), 2000) }, 10*time.Second, 1*time.Second, "time travel failed to rollback offset") } @@ -952,7 +957,7 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { require.NoError(t, err) twoGbExtraAsLeft := (diskSpace.Available - (2 * 1024 * 1024)) / 1024 - twoGbExtraAsPercent := (((2 * 1024 * 1024) + diskSpace.Used) * 100 / diskSpace.Capacity) + oneGbExtraAsPercent := (((1 * 1024 * 1024) + diskSpace.Used) * 100 / diskSpace.Capacity) type testCase struct { name string @@ -968,11 +973,11 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { { name: "fill disk with percentage (fallocate)", mode: diskfill.Percentage, - size: int(twoGbExtraAsPercent), + size: int(oneGbExtraAsPercent), blockSize: 0, method: diskfill.AtOnce, - wantedFileSize: 2 * 1024, - allowedDelta: 512, + wantedFileSize: 1 * 1024, + allowedDelta: 256, }, { name: "fill disk with megabytes to fill (fallocate)", @@ -995,18 +1000,17 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { { name: "fill disk with percentage (dd)", mode: diskfill.Percentage, - size: int(twoGbExtraAsPercent), - blockSize: 5, + size: int(oneGbExtraAsPercent), + blockSize: 0, method: diskfill.OverTime, - wantedFileSize: 2 * 1024, - - allowedDelta: 512, + wantedFileSize: 1 * 1024, + allowedDelta: 256, }, { name: "fill disk with megabytes to fill (dd)", mode: diskfill.MBToFill, size: 4 * 1024, // 4GB - blockSize: 1, + blockSize: 4, method: diskfill.OverTime, wantedFileSize: 4 * 1024, allowedDelta: 0, @@ -1015,7 +1019,7 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { name: "fill disk with megabytes left (dd)", mode: diskfill.MBLeft, size: int(twoGbExtraAsLeft), - blockSize: 5, + blockSize: 16, method: diskfill.OverTime, wantedFileSize: 2 * 1024, allowedDelta: 512, @@ -1024,7 +1028,7 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { name: "fill disk with bigger blocksize (dd)", mode: diskfill.MBToFill, size: 4 * 1024, // 4GB - blockSize: 6 * 1024, // 2GB + blockSize: 6 * 1024, method: diskfill.OverTime, wantedFileSize: 4 * 1024, allowedDelta: 512, From bb1c562dd7f41ffc18ed6bbbbf67be40449c13ba Mon Sep 17 00:00:00 2001 From: Johannes Edmeier Date: Sat, 1 Nov 2025 16:31:51 +0100 Subject: [PATCH 7/9] test: timetravel needs external timesource --- e2e/integration_test.go | 64 +++++++++++++++++++++++++++-------------- go.mod | 1 + go.sum | 2 ++ 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/e2e/integration_test.go b/e2e/integration_test.go index fe49b4c1..46130fdc 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -19,6 +19,7 @@ import ( "testing" "time" + "github.com/beevik/ntp" "github.com/rs/zerolog/log" "github.com/steadybit/action-kit/go/action_kit_api/v2" "github.com/steadybit/action-kit/go/action_kit_commons/diskfill" @@ -29,6 +30,7 @@ import ( "github.com/steadybit/extension-kit/extutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/sync/errgroup" ) var ( @@ -280,42 +282,62 @@ func testTimeTravel(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { DisableNtp: true, } - containerTime := getContainerTime(t, m, e) - offsetContainer := time.Until(getContainerTime(t, m, e)) - fmt.Printf("before containerTime %s offsetContainer %s\n", containerTime, offsetContainer) + normalOffset, err := getExtensionPodTimeOffset(m, e) + require.NoError(t, err) action, err := e.RunAction(exthost.BaseActionID+".timetravel", getTarget(m), config, nil) defer func() { _ = action.Cancel() }() require.NoError(t, err) assert.EventuallyWithT(t, func(c *assert.CollectT) { - containerTime := getContainerTime(t, m, e) - diff := time.Until(containerTime) - adjustedDiff := diff + offsetContainer - assert.InDelta(t, config.Offset, adjustedDiff.Milliseconds(), 2000) - fmt.Printf("during containerTime %s offsetContainer %s diff %s adjustedDiff %s\n", containerTime, offsetContainer, diff, adjustedDiff) + adjustedOffset, err := getExtensionPodTimeOffset(m, e) + require.NoError(t, err) + assert.InDelta(t, config.Offset, (normalOffset + adjustedOffset).Milliseconds(), 2000) }, 10*time.Second, 1*time.Second, "time travel failed to apply offset") // rollback require.NoError(t, action.Cancel()) assert.EventuallyWithT(t, func(c *assert.CollectT) { - containerTime := getContainerTime(t, m, e) - diff := time.Until(containerTime) - adjustedDiff := diff + offsetContainer - assert.InDelta(t, 0, adjustedDiff.Milliseconds(), 2000) + adjustedOffset, err := getExtensionPodTimeOffset(m, e) + require.NoError(t, err) + assert.InDelta(t, 0, (normalOffset + adjustedOffset).Milliseconds(), 2000) }, 10*time.Second, 1*time.Second, "time travel failed to rollback offset") } -func getContainerTime(t *testing.T, m *e2e.Minikube, e *e2e.Extension) time.Time { - out, err := m.PodExec(e.Pod, "extension", "date", "+%s") - if err != nil { - t.Fatal(err) - } - containerSecondsSinceEpoch := extutil.ToInt64(strings.TrimSpace(out)) - if containerSecondsSinceEpoch == 0 { - t.Fatal("could not parse container time") +func getExtensionPodTimeOffset(m *e2e.Minikube, e *e2e.Extension) (time.Duration, error) { + var g errgroup.Group + chPodTime := make(chan time.Time, 1) + chNtpTime := make(chan time.Time, 1) + + g.Go(func() error { + out, err := m.PodExec(e.Pod, "extension", "date", "+%s") + if err != nil { + return err + } + + epoch := extutil.ToInt64(strings.TrimSpace(out)) + if epoch == 0 { + return fmt.Errorf("failed to get container time") + } + + chPodTime <- time.Unix(epoch, 0) + return nil + }) + + g.Go(func() error { + ntpTime, err := ntp.Time("pool.ntp.org") + if err != nil { + return err + } + chNtpTime <- ntpTime + return nil + }) + + if err := g.Wait(); err != nil { + return 0, err } - return time.Unix(containerSecondsSinceEpoch, 0) + + return (<-chPodTime).Sub(<-chNtpTime), nil } func validateDiscovery(t *testing.T, _ *e2e.Minikube, e *e2e.Extension) { diff --git a/go.mod b/go.mod index 38c2c1a2..073f850e 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ toolchain go1.24.1 require ( github.com/KimMachineGun/automemlimit v0.7.5 + github.com/beevik/ntp v1.5.0 github.com/elastic/go-sysinfo v1.15.4 github.com/google/uuid v1.6.0 github.com/kelseyhightower/envconfig v1.4.0 diff --git a/go.sum b/go.sum index ee1d8824..fb4e38d5 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7D github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/beevik/ntp v1.5.0 h1:y+uj/JjNwlY2JahivxYvtmv4ehfi3h74fAuABB9ZSM4= +github.com/beevik/ntp v1.5.0/go.mod h1:mJEhBrwT76w9D+IfOEGvuzyuudiW9E52U2BaTrMOYow= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From cb1c193f38f45cf839e418acd17cab2aea3cf45b Mon Sep 17 00:00:00 2001 From: Johannes Edmeier Date: Sat, 1 Nov 2025 21:12:58 +0100 Subject: [PATCH 8/9] .... --- e2e/integration_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/integration_test.go b/e2e/integration_test.go index 46130fdc..872b5172 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -996,7 +996,7 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { name: "fill disk with percentage (fallocate)", mode: diskfill.Percentage, size: int(oneGbExtraAsPercent), - blockSize: 0, + blockSize: 64, method: diskfill.AtOnce, wantedFileSize: 1 * 1024, allowedDelta: 256, @@ -1023,7 +1023,7 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { name: "fill disk with percentage (dd)", mode: diskfill.Percentage, size: int(oneGbExtraAsPercent), - blockSize: 0, + blockSize: 64, method: diskfill.OverTime, wantedFileSize: 1 * 1024, allowedDelta: 256, From 5089ad9b1a251d84994b5432e7f21d40ce73b45d Mon Sep 17 00:00:00 2001 From: Johannes Edmeier Date: Sat, 1 Nov 2025 21:50:57 +0100 Subject: [PATCH 9/9] .... --- e2e/integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/integration_test.go b/e2e/integration_test.go index 872b5172..4026422c 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -979,7 +979,7 @@ func testFillDisk(t *testing.T, m *e2e.Minikube, e *e2e.Extension) { require.NoError(t, err) twoGbExtraAsLeft := (diskSpace.Available - (2 * 1024 * 1024)) / 1024 - oneGbExtraAsPercent := (((1 * 1024 * 1024) + diskSpace.Used) * 100 / diskSpace.Capacity) + oneGbExtraAsPercent := ((1.5 * 1024 * 1024) + diskSpace.Used) * 100 / diskSpace.Capacity type testCase struct { name string