forked from jstarks/hcsshim
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request microsoft#981 from anmaxvl/maksiman/tests/scale-cp…
…u-limits Add test for ScaleCPULimitsToSandbox runtime config
- Loading branch information
Showing
5 changed files
with
165 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package cri_containerd | ||
|
||
import ( | ||
"context" | ||
"math" | ||
"runtime" | ||
"strconv" | ||
"testing" | ||
"time" | ||
|
||
criruntime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" | ||
) | ||
|
||
const imageWindowsMaxCPUWorkload = "cplatpublic.azurecr.io/golang-1.16.2-nanoserver-1809:max-cpu-workload" | ||
|
||
func Test_Scale_CPU_Limits_To_Sandbox(t *testing.T) { | ||
requireFeatures(t, featureWCOWHypervisor) | ||
|
||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
|
||
client := newTestRuntimeClient(t) | ||
podReq := getRunPodSandboxRequest(t, wcowHypervisor17763RuntimeHandler) | ||
podID := runPodSandbox(t, client, ctx, podReq) | ||
defer removePodSandbox(t, client, ctx, podID) | ||
|
||
pullRequiredImages(t, []string{imageWindowsMaxCPUWorkload}) | ||
|
||
cmd := []string{"cmd", "/c", `C:\load_cpu.exe`} | ||
contReq := getCreateContainerRequest(podID, "nanoserver-load-cpu", imageWindowsMaxCPUWorkload, cmd, podReq.Config) | ||
// set the limit to (roughly) 1 processor | ||
processorLimit := 10000 / runtime.NumCPU() | ||
contReq.Config.Annotations = map[string]string{ | ||
"io.microsoft.container.processor.limit": strconv.Itoa(processorLimit), | ||
} | ||
|
||
contID := createContainer(t, client, ctx, contReq) | ||
defer removeContainer(t, client, ctx, contID) | ||
startContainer(t, client, ctx, contID) | ||
defer stopContainer(t, client, ctx, contID) | ||
|
||
statsRequest := &criruntime.ContainerStatsRequest{ | ||
ContainerId: contID, | ||
} | ||
|
||
// baseline container stats request | ||
initialResponse, err := client.ContainerStats(ctx, statsRequest) | ||
if err != nil { | ||
t.Fatalf("error getting initial container stats: %s", err) | ||
} | ||
|
||
// give it 5 seconds for a better average, with just 1 second, the measurements | ||
// are consistently 25-30% higher than expected | ||
time.Sleep(5 * time.Second) | ||
|
||
// final container stats request | ||
finalResponse, err := client.ContainerStats(ctx, statsRequest) | ||
if err != nil { | ||
t.Fatalf("error getting container new container stats: %s", err) | ||
} | ||
|
||
// Estimate CPU usage by dividing total usage in nanoseconds by time passed in nanoseconds | ||
oldStats := initialResponse.GetStats().GetCpu() | ||
newStats := finalResponse.GetStats().GetCpu() | ||
deltaTime := newStats.GetTimestamp() - oldStats.GetTimestamp() | ||
deltaUsage := newStats.GetUsageCoreNanoSeconds().GetValue() - oldStats.GetUsageCoreNanoSeconds().GetValue() | ||
usagePercentage := float64(deltaUsage) / float64(deltaTime) * 100 | ||
t.Logf("container CPU usage percentage: %f", usagePercentage) | ||
if math.Abs(usagePercentage-100) > 10 { | ||
t.Fatalf("expected CPU usage around 100 percent, got %f instead. Make sure that ScaleCpuLimitsToSandbox runtime option is set to true", usagePercentage) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# This Dockerfile builds a docker image based on golang:1.16.2-nanoserver-1809. | ||
# The image is used in test/cri-containerd/scale_cpu_limits_to_sandbox.go. | ||
# If any changes are made to this Dockerfile, make sure to update the tests | ||
# accordingly. | ||
|
||
# Base image | ||
FROM golang:1.16.2-nanoserver-1809 | ||
|
||
# Get administrator privileges | ||
USER ContainerAdministrator | ||
|
||
# Put everything in the root directory | ||
WORKDIR / | ||
|
||
# Copy the source file | ||
COPY main.go . | ||
|
||
# Build binary | ||
RUN go build -o load_cpu.exe main.go |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"runtime" | ||
"strconv" | ||
"time" | ||
) | ||
|
||
const defaultDuration = 5 | ||
|
||
// This implementation is a simplified version of https://github.com/vikyd/go-cpu-load | ||
func main() { | ||
cores := runtime.NumCPU() | ||
runtime.GOMAXPROCS(cores) | ||
|
||
loadDuration := defaultDuration | ||
// Check if duration has been passed explicitly | ||
if len(os.Args) > 1 { | ||
var err error | ||
loadDuration, err = strconv.Atoi(os.Args[1]) | ||
if err != nil { | ||
_, _ = fmt.Fprintf(os.Stderr, "first argument must be integer: %s", err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
for i := 0; i < cores; i++ { | ||
go func() { | ||
runtime.LockOSThread() | ||
defer runtime.UnlockOSThread() | ||
|
||
begin := time.Now() | ||
for { | ||
if time.Now().Sub(begin) > time.Duration(loadDuration)*time.Second { | ||
break | ||
} | ||
} | ||
}() | ||
} | ||
time.Sleep(time.Duration(loadDuration) * time.Second) | ||
} |