Skip to content

Commit f00e273

Browse files
author
Peng Yin
committed
windows: adding functional test for cpu percent
1 parent eee587d commit f00e273

File tree

10 files changed

+116
-31
lines changed

10 files changed

+116
-31
lines changed

agent/api/task_windows.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@ import (
2727
const (
2828
//memorySwappinessDefault is the expected default value for this platform
2929
memorySwappinessDefault = -1
30+
// cpuSharesPerCore represents the cpu shares of a cpu core in docker
31+
cpuSharesPerCore = 1024
32+
percentageFactor = 100
3033
)
3134

32-
var cpuShareScaleFactor = runtime.NumCPU() * 1024
35+
var cpuShareScaleFactor = runtime.NumCPU() * cpuSharesPerCore
3336

3437
// adjustForPlatform makes Windows-specific changes to the task after unmarshal
3538
func (task *Task) adjustForPlatform(cfg *config.Config) {
@@ -63,7 +66,7 @@ func getCanonicalPath(path string) string {
6366
func (task *Task) platformHostConfigOverride(hostConfig *docker.HostConfig) error {
6467
task.overrideDefaultMemorySwappiness(hostConfig)
6568
// Convert the CPUShares to CPUPercent
66-
hostConfig.CPUPercent = hostConfig.CPUShares * 100 / int64(cpuShareScaleFactor)
69+
hostConfig.CPUPercent = hostConfig.CPUShares * percentageFactor / int64(cpuShareScaleFactor)
6770
if hostConfig.CPUPercent != 0 {
6871
// Only unset the CPUShares if the CPUPercent has valid value
6972
hostConfig.CPUShares = 0

agent/api/task_windows_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,10 @@ func TestWindowsPlatformHostConfigOverride(t *testing.T) {
112112

113113
task := &Task{}
114114

115-
hostConfig := &docker.HostConfig{CPUShares: 1024}
115+
hostConfig := &docker.HostConfig{CPUShares: int64(1 * cpuSharesPerCore)}
116116

117117
task.platformHostConfigOverride(hostConfig)
118-
assert.Equal(t, int64(102400)/int64(cpuShareScaleFactor), hostConfig.CPUPercent)
118+
assert.Equal(t, int64(1*cpuSharesPerCore*percentageFactor)/int64(cpuShareScaleFactor), hostConfig.CPUPercent)
119119
assert.Equal(t, int64(0), hostConfig.CPUShares)
120120
assert.EqualValues(t, expectedMemorySwappinessDefault, hostConfig.MemorySwappiness)
121121

Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
{
2-
"family": "ecsftest-telemetry-windows",
2+
"family": "ecsftest-windows-telemetry",
33
"containerDefinitions": [{
4-
"image": "microsoft/iis:latest",
5-
"name": "http_server",
6-
"cpu": 100,
7-
"memory": 500,
8-
"command": ["powershell", "-c", "New-Item -Path C:\\inetpub\\wwwroot\\index.html -Type file -Value '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p>'; C:\\ServiceMonitor.exe w3svc"]
4+
"image": "amazon/amazon-ecs-windows-cpupercent-test:make",
5+
"name": "windows-cpu-percent",
6+
"cpu": $$$$CPUSHARE$$$$,
7+
"memory": 500
98
}]
109
}

agent/functional_tests/tests/functionaltests_unix_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -383,11 +383,11 @@ func TestTelemetry(t *testing.T) {
383383
time.Sleep(waitMetricsInCloudwatchDuration)
384384

385385
cwclient := cloudwatch.New(session.New(), aws.NewConfig().WithRegion(*ECS.Config.Region))
386-
err = VerifyMetrics(cwclient, params, true)
386+
_, err = VerifyMetrics(cwclient, params, true)
387387
assert.NoError(t, err, "Before task running, verify metrics for CPU utilization failed")
388388

389389
params.MetricName = aws.String("MemoryUtilization")
390-
err = VerifyMetrics(cwclient, params, true)
390+
_, err = VerifyMetrics(cwclient, params, true)
391391
assert.NoError(t, err, "Before task running, verify metrics for memory utilization failed")
392392

393393
testTask, err := agent.StartTask(t, "telemetry")
@@ -400,11 +400,11 @@ func TestTelemetry(t *testing.T) {
400400
params.EndTime = aws.Time(RoundTimeUp(time.Now(), time.Minute).UTC())
401401
params.StartTime = aws.Time((*params.EndTime).Add(-waitMetricsInCloudwatchDuration).UTC())
402402
params.MetricName = aws.String("CPUUtilization")
403-
err = VerifyMetrics(cwclient, params, false)
403+
_, err = VerifyMetrics(cwclient, params, false)
404404
assert.NoError(t, err, "Task is running, verify metrics for CPU utilization failed")
405405

406406
params.MetricName = aws.String("MemoryUtilization")
407-
err = VerifyMetrics(cwclient, params, false)
407+
_, err = VerifyMetrics(cwclient, params, false)
408408
assert.NoError(t, err, "Task is running, verify metrics for memory utilization failed")
409409

410410
err = testTask.Stop()
@@ -417,11 +417,11 @@ func TestTelemetry(t *testing.T) {
417417
params.EndTime = aws.Time(RoundTimeUp(time.Now(), time.Minute).UTC())
418418
params.StartTime = aws.Time((*params.EndTime).Add(-waitMetricsInCloudwatchDuration).UTC())
419419
params.MetricName = aws.String("CPUUtilization")
420-
err = VerifyMetrics(cwclient, params, true)
420+
_, err = VerifyMetrics(cwclient, params, true)
421421
assert.NoError(t, err, "Task stopped: verify metrics for CPU utilization failed")
422422

423423
params.MetricName = aws.String("MemoryUtilization")
424-
err = VerifyMetrics(cwclient, params, true)
424+
_, err = VerifyMetrics(cwclient, params, true)
425425
assert.NoError(t, err, "Task stopped, verify metrics for memory utilization failed")
426426
}
427427

agent/functional_tests/tests/functionaltests_windows_test.go

+18-7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package functional_tests
1818
import (
1919
"fmt"
2020
"os"
21+
"runtime"
22+
"strconv"
2123
"strings"
2224
"testing"
2325
"time"
@@ -41,6 +43,7 @@ const (
4143
logDriverTaskDefinition = "logdriver-jsonfile-windows"
4244
cleanupTaskDefinition = "cleanup-windows"
4345
networkModeTaskDefinition = "network-mode-windows"
46+
cpuSharesPerCore = 1024
4447
)
4548

4649
// TestAWSLogsDriver verifies that container logs are sent to Amazon CloudWatch Logs with awslogs as the log driver
@@ -266,14 +269,20 @@ func TestTelemetry(t *testing.T) {
266269
time.Sleep(waitMetricsInCloudwatchDuration)
267270

268271
cwclient := cloudwatch.New(session.New(), aws.NewConfig().WithRegion(*ECS.Config.Region))
269-
err = VerifyMetrics(cwclient, params, true)
272+
_, err = VerifyMetrics(cwclient, params, true)
270273
assert.NoError(t, err, "Before task running, verify metrics for CPU utilization failed")
271274

272275
params.MetricName = aws.String("MemoryUtilization")
273-
err = VerifyMetrics(cwclient, params, true)
276+
_, err = VerifyMetrics(cwclient, params, true)
274277
assert.NoError(t, err, "Before task running, verify metrics for memory utilization failed")
275278

276-
testTask, err := agent.StartTask(t, "telemetry-windows")
279+
cpuNum := runtime.NumCPU()
280+
281+
tdOverrides := make(map[string]string)
282+
// Set the container cpu percentage 25%
283+
tdOverrides["$$$$CPUSHARE$$$$"] = strconv.Itoa(int(float64(cpuNum*cpuSharesPerCore) * 0.25))
284+
285+
testTask, err := agent.StartTaskWithTaskDefinitionOverrides(t, "telemetry-windows", tdOverrides)
277286
require.NoError(t, err, "Failed to start telemetry task")
278287
// Wait for the task to run and the agent to send back metrics
279288
err = testTask.WaitRunning(waitTaskStateChangeDuration)
@@ -283,11 +292,13 @@ func TestTelemetry(t *testing.T) {
283292
params.EndTime = aws.Time(RoundTimeUp(time.Now(), time.Minute).UTC())
284293
params.StartTime = aws.Time((*params.EndTime).Add(-waitMetricsInCloudwatchDuration).UTC())
285294
params.MetricName = aws.String("CPUUtilization")
286-
err = VerifyMetrics(cwclient, params, false)
295+
metrics, err := VerifyMetrics(cwclient, params, false)
287296
assert.NoError(t, err, "Task is running, verify metrics for CPU utilization failed")
297+
// Also verify the cpu usage is around 25%
298+
assert.InDelta(t, 0.25, *metrics.Average, 0.05)
288299

289300
params.MetricName = aws.String("MemoryUtilization")
290-
err = VerifyMetrics(cwclient, params, false)
301+
_, err = VerifyMetrics(cwclient, params, false)
291302
assert.NoError(t, err, "Task is running, verify metrics for memory utilization failed")
292303

293304
err = testTask.Stop()
@@ -300,10 +311,10 @@ func TestTelemetry(t *testing.T) {
300311
params.EndTime = aws.Time(RoundTimeUp(time.Now(), time.Minute).UTC())
301312
params.StartTime = aws.Time((*params.EndTime).Add(-waitMetricsInCloudwatchDuration).UTC())
302313
params.MetricName = aws.String("CPUUtilization")
303-
err = VerifyMetrics(cwclient, params, true)
314+
_, err = VerifyMetrics(cwclient, params, true)
304315
assert.NoError(t, err, "Task stopped: verify metrics for CPU utilization failed")
305316

306317
params.MetricName = aws.String("MemoryUtilization")
307-
err = VerifyMetrics(cwclient, params, true)
318+
_, err = VerifyMetrics(cwclient, params, true)
308319
assert.NoError(t, err, "Task stopped, verify metrics for memory utilization failed")
309320
}

agent/functional_tests/util/utils.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -266,36 +266,36 @@ func DeleteCluster(t *testing.T, clusterName string) {
266266

267267
// VerifyMetrics whether the response is as expected
268268
// the expected value can be 0 or positive
269-
func VerifyMetrics(cwclient *cloudwatch.CloudWatch, params *cloudwatch.GetMetricStatisticsInput, idleCluster bool) error {
269+
func VerifyMetrics(cwclient *cloudwatch.CloudWatch, params *cloudwatch.GetMetricStatisticsInput, idleCluster bool) (*cloudwatch.Datapoint, error) {
270270
resp, err := cwclient.GetMetricStatistics(params)
271271
if err != nil {
272-
return fmt.Errorf("Error getting metrics of cluster: %v", err)
272+
return nil, fmt.Errorf("Error getting metrics of cluster: %v", err)
273273
}
274274

275275
if resp == nil || resp.Datapoints == nil {
276-
return fmt.Errorf("Cloudwatch get metrics failed, returned null")
276+
return nil, fmt.Errorf("Cloudwatch get metrics failed, returned null")
277277
}
278278
metricsCount := len(resp.Datapoints)
279279
if metricsCount == 0 {
280-
return fmt.Errorf("No datapoints returned")
280+
return nil, fmt.Errorf("No datapoints returned")
281281
}
282282

283283
datapoint := resp.Datapoints[metricsCount-1]
284284
// Samplecount is always expected to be "1" for cluster metrics
285285
if *datapoint.SampleCount != 1.0 {
286-
return fmt.Errorf("Incorrect SampleCount %f, expected 1", *datapoint.SampleCount)
286+
return nil, fmt.Errorf("Incorrect SampleCount %f, expected 1", *datapoint.SampleCount)
287287
}
288288

289289
if idleCluster {
290290
if *datapoint.Average != 0.0 {
291-
return fmt.Errorf("non-zero utilization for idle cluster")
291+
return nil, fmt.Errorf("non-zero utilization for idle cluster")
292292
}
293293
} else {
294294
if *datapoint.Average == 0.0 {
295-
return fmt.Errorf("utilization is zero for non-idle cluster")
295+
return nil, fmt.Errorf("utilization is zero for non-idle cluster")
296296
}
297297
}
298-
return nil
298+
return datapoint, nil
299299
}
300300

301301
// ResolveTaskDockerID determines the Docker ID for a container within a given

misc/windows-cpupercent/build.ps1

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
# not use this file except in compliance with the License. A copy of the
5+
# License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is distributed
10+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
# express or implied. See the License for the specific language governing
12+
# permissions and limitations under the License.
13+
14+
docker build -t "amazon/amazon-ecs-windows-cpupercent-test:make" -f "${PSScriptRoot}/windows.dockerfile" ${PSScriptRoot}

misc/windows-cpupercent/main.go

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
14+
package main
15+
16+
import (
17+
"crypto/md5"
18+
"flag"
19+
"fmt"
20+
)
21+
22+
func main() {
23+
concurrency := flag.Int("concurrency", 1, "amount of concurrency")
24+
flag.Parse()
25+
neverdie := make(chan struct{})
26+
27+
fmt.Printf("Hogging CPU with concurrency %d\n", *concurrency)
28+
for i := 0; i < *concurrency; i++ {
29+
go func() {
30+
md5hash := md5.New()
31+
for {
32+
md5hash.Write([]byte{0})
33+
}
34+
}()
35+
}
36+
<-neverdie
37+
}
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
# not use this file except in compliance with the License. A copy of the
5+
# License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is distributed
10+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
# express or implied. See the License for the specific language governing
12+
# permissions and limitations under the License.
13+
FROM golang:nanoserver
14+
15+
WORKDIR /gopath
16+
COPY main.go .
17+
18+
RUN go build -o cpuhog main.go
19+
ENTRYPOINT ["./cpuhog"]
20+
CMD [ "-concurrency", "1000" ]

scripts/run-functional-tests.ps1

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
Invoke-Expression "${PSScriptRoot}\..\misc\windows-iam\Setup_Iam.ps1"
1515
Invoke-Expression "${PSScriptRoot}\..\misc\windows-listen80\Setup_Listen80.ps1"
16+
Invoke-Expression "${PSScriptRoot}\..\misc\windows-cpupercent\build.ps1"
1617

1718
# Run the tests
1819
$cwd = (pwd).Path

0 commit comments

Comments
 (0)