Skip to content

Commit

Permalink
fix: Add support for new cloud URLs (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
zerok authored Dec 6, 2023
1 parent dd1023d commit 6ddd79f
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 49 deletions.
4 changes: 2 additions & 2 deletions pkg/handlers/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ const (
emojiFailure = ":red_circle:"
)

// https://regex101.com/r/Pn7VUB/1
var outputRegex = regexp.MustCompile(`output: cloud \((?P<url>https:\/\/app\.k6\.io\/runs\/\d+)\)`)
// https://regex101.com/r/OZwd8Y/1
var outputRegex = regexp.MustCompile(`output: cloud \((?P<url>https:\/\/((app\.k6\.io)|([^/]+\.grafana.net\/a\/k6-app))\/runs\/\d+)\)`)

type launchPayload struct {
flaggerWebhook
Expand Down
116 changes: 70 additions & 46 deletions pkg/handlers/launch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,55 +158,73 @@ func TestNewLaunchPayload(t *testing.T) {
}

func TestLaunchAndWaitCloud(t *testing.T) {
// Initialize controller
_, k6Client, slackClient, testRun, handler := setupHandler(t)

// Expected calls
// * Start the run
fullResults, resultParts := getTestOutput(t)
var bufferWriter io.Writer
k6Client.EXPECT().Start("my-script", true, nil, gomock.Any()).DoAndReturn(func(scriptContent string, upload bool, envVars map[string]string, outputWriter io.Writer) (k6.TestRun, error) {
bufferWriter = outputWriter
outputWriter.Write([]byte(resultParts[0]))
return testRun, nil
})

// * Send the initial slack message
channelMap := map[string]string{"C1234": "ts1", "C12345": "ts2"}
slackClient.EXPECT().SendMessages(
[]string{"test", "test2"},
":warning: Load testing of `test-name` in namespace `test-space` has started",
"extra context\nCloud URL: <https://app.k6.io/runs/1157843>",
).Return(channelMap, nil)
tests := map[string]struct {
k6OutputFile string
cloudURL string
}{
"legacy-cloud-url": {
k6OutputFile: "testdata/k6-output-legacy.txt",
cloudURL: "https://app.k6.io/runs/1157843",
},
"grafana-cloud-url": {
k6OutputFile: "testdata/k6-output.txt",
cloudURL: "https://somewhere.grafana.net/a/k6-app/runs/1157843",
},
}

// * Wait for the command to finish
testRun.EXPECT().Wait().DoAndReturn(func() error {
bufferWriter.Write([]byte("running" + resultParts[1]))
return nil
})
for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
// Initialize controller
_, k6Client, slackClient, testRun, handler := setupHandler(t)

// Expected calls
// * Start the run
fullResults, resultParts := getTestOutputFromFile(t, test.k6OutputFile)
var bufferWriter io.Writer
k6Client.EXPECT().Start("my-script", true, nil, gomock.Any()).DoAndReturn(func(scriptContent string, upload bool, envVars map[string]string, outputWriter io.Writer) (k6.TestRun, error) {
bufferWriter = outputWriter
outputWriter.Write([]byte(resultParts[0]))
return testRun, nil
})

// * Send the initial slack message
channelMap := map[string]string{"C1234": "ts1", "C12345": "ts2"}
slackClient.EXPECT().SendMessages(
[]string{"test", "test2"},
":warning: Load testing of `test-name` in namespace `test-space` has started",
fmt.Sprintf("extra context\nCloud URL: <%s>", test.cloudURL),
).Return(channelMap, nil)

// * Wait for the command to finish
testRun.EXPECT().Wait().DoAndReturn(func() error {
bufferWriter.Write([]byte("running" + resultParts[1]))
return nil
})

// * Upload the results file and update the slack message
slackClient.EXPECT().AddFileToThreads(
channelMap,
"k6-results.txt",
string(fullResults),
).Return(nil)
slackClient.EXPECT().UpdateMessages(
channelMap,
":large_green_circle: Load testing of `test-name` in namespace `test-space` has succeeded",
fmt.Sprintf("extra context\nCloud URL: <%s>", test.cloudURL),
).Return(nil)

// * Upload the results file and update the slack message
slackClient.EXPECT().AddFileToThreads(
channelMap,
"k6-results.txt",
string(fullResults),
).Return(nil)
slackClient.EXPECT().UpdateMessages(
channelMap,
":large_green_circle: Load testing of `test-name` in namespace `test-space` has succeeded",
"extra context\nCloud URL: <https://app.k6.io/runs/1157843>",
).Return(nil)
// Make request
request := &http.Request{
Body: ioutil.NopCloser(strings.NewReader(`{"name": "test-name", "namespace": "test-space", "phase": "pre-rollout", "metadata": {"script": "my-script", "upload_to_cloud": "true", "slack_channels": "test,test2", "notification_context": "extra context"}}`)),
}
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, request)

// Make request
request := &http.Request{
Body: ioutil.NopCloser(strings.NewReader(`{"name": "test-name", "namespace": "test-space", "phase": "pre-rollout", "metadata": {"script": "my-script", "upload_to_cloud": "true", "slack_channels": "test,test2", "notification_context": "extra context"}}`)),
// Expected response
assert.Equal(t, fullResults, rr.Body.Bytes())
assert.Equal(t, 200, rr.Result().StatusCode)
})
}
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, request)

// Expected response
assert.Equal(t, fullResults, rr.Body.Bytes())
assert.Equal(t, 200, rr.Result().StatusCode)
}

func TestSlackFailuresDontAbort(t *testing.T) {
Expand Down Expand Up @@ -630,7 +648,13 @@ func setupHandlerWithKubernetesObjects(t *testing.T, expectedKubernetesObjects .
func getTestOutput(t *testing.T) ([]byte, []string) {
t.Helper()

fullResults, err := os.ReadFile("testdata/k6-output.txt")
return getTestOutputFromFile(t, "testdata/k6-output.txt")
}

func getTestOutputFromFile(t *testing.T, filename string) ([]byte, []string) {
t.Helper()

fullResults, err := os.ReadFile(filename)
require.NoError(t, err)
resultParts := strings.SplitN(string(fullResults), "running", 2)

Expand Down
125 changes: 125 additions & 0 deletions pkg/handlers/testdata/k6-output-legacy.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@

/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io

execution: local
script: /tmp/k6-script504149289
output: cloud (https://app.k6.io/runs/1157843)

scenarios: (100.00%) 1 scenario, 2 max VUs, 1m0s max duration (incl. graceful stop):
* default: 2 looping VUs for 30s (gracefulStop: 30s)


running (0m00.7s), 2/2 VUs, 9 complete and 0 interrupted iterations
default [ 2% ] 2 VUs 00.7s/30s

running (0m01.4s), 2/2 VUs, 14 complete and 0 interrupted iterations
default [ 5% ] 2 VUs 01.4s/30s

running (0m02.4s), 2/2 VUs, 34 complete and 0 interrupted iterations
default [ 8% ] 2 VUs 02.4s/30s

running (0m03.4s), 2/2 VUs, 54 complete and 0 interrupted iterations
default [ 11% ] 2 VUs 03.4s/30s

running (0m04.4s), 2/2 VUs, 74 complete and 0 interrupted iterations
default [ 15% ] 2 VUs 04.4s/30s

running (0m05.4s), 2/2 VUs, 94 complete and 0 interrupted iterations
default [ 18% ] 2 VUs 05.4s/30s

running (0m06.4s), 2/2 VUs, 114 complete and 0 interrupted iterations
default [ 21% ] 2 VUs 06.4s/30s

running (0m07.4s), 2/2 VUs, 134 complete and 0 interrupted iterations
default [ 25% ] 2 VUs 07.4s/30s

running (0m08.4s), 2/2 VUs, 152 complete and 0 interrupted iterations
default [ 28% ] 2 VUs 08.4s/30s

running (0m09.4s), 2/2 VUs, 172 complete and 0 interrupted iterations
default [ 31% ] 2 VUs 09.4s/30s

running (0m10.4s), 2/2 VUs, 192 complete and 0 interrupted iterations
default [ 35% ] 2 VUs 10.4s/30s

running (0m11.4s), 2/2 VUs, 212 complete and 0 interrupted iterations
default [ 38% ] 2 VUs 11.4s/30s

running (0m12.4s), 2/2 VUs, 232 complete and 0 interrupted iterations
default [ 41% ] 2 VUs 12.4s/30s

running (0m13.4s), 2/2 VUs, 252 complete and 0 interrupted iterations
default [ 45% ] 2 VUs 13.4s/30s

running (0m14.4s), 2/2 VUs, 272 complete and 0 interrupted iterations
default [ 48% ] 2 VUs 14.4s/30s

running (0m15.4s), 2/2 VUs, 292 complete and 0 interrupted iterations
default [ 51% ] 2 VUs 15.4s/30s

running (0m16.4s), 2/2 VUs, 312 complete and 0 interrupted iterations
default [ 55% ] 2 VUs 16.4s/30s

running (0m17.4s), 2/2 VUs, 330 complete and 0 interrupted iterations
default [ 58% ] 2 VUs 17.4s/30s

running (0m18.4s), 2/2 VUs, 350 complete and 0 interrupted iterations
default [ 61% ] 2 VUs 18.4s/30s

running (0m19.4s), 2/2 VUs, 370 complete and 0 interrupted iterations
default [ 65% ] 2 VUs 19.4s/30s

running (0m20.4s), 2/2 VUs, 390 complete and 0 interrupted iterations
default [ 68% ] 2 VUs 20.4s/30s

running (0m21.4s), 2/2 VUs, 410 complete and 0 interrupted iterations
default [ 71% ] 2 VUs 21.4s/30s

running (0m22.4s), 2/2 VUs, 430 complete and 0 interrupted iterations
default [ 75% ] 2 VUs 22.4s/30s

running (0m23.4s), 2/2 VUs, 450 complete and 0 interrupted iterations
default [ 78% ] 2 VUs 23.4s/30s

running (0m24.4s), 2/2 VUs, 470 complete and 0 interrupted iterations
default [ 81% ] 2 VUs 24.4s/30s

running (0m25.4s), 2/2 VUs, 490 complete and 0 interrupted iterations
default [ 85% ] 2 VUs 25.4s/30s

running (0m26.4s), 2/2 VUs, 508 complete and 0 interrupted iterations
default [ 88% ] 2 VUs 26.4s/30s

running (0m27.4s), 2/2 VUs, 528 complete and 0 interrupted iterations
default [ 91% ] 2 VUs 27.4s/30s

running (0m28.4s), 2/2 VUs, 548 complete and 0 interrupted iterations
default [ 95% ] 2 VUs 28.4s/30s

running (0m29.4s), 2/2 VUs, 568 complete and 0 interrupted iterations
default [ 98% ] 2 VUs 29.4s/30s

running (0m30.1s), 0/2 VUs, 582 complete and 0 interrupted iterations
default ✓ [ 100% ] 2 VUs 30s

data_received..................: 814 kB 27 kB/s
data_sent......................: 61 kB 2.0 kB/s
http_req_blocked...............: avg=21.27µs min=3.21µs med=5.76µs max=3.8ms p(90)=7.56µs p(95)=8.39µs
http_req_connecting............: avg=5.02µs min=0s med=0s max=941.43µs p(90)=0s p(95)=0s
✓ http_req_duration..............: avg=1.02ms min=217.94µs med=383.29µs max=216.18ms p(90)=469.15µs p(95)=524.76µs
{ expected_response:true }...: avg=1.02ms min=217.94µs med=383.29µs max=216.18ms p(90)=469.15µs p(95)=524.76µs
http_req_failed................: 0.00% ✓ 0 ✗ 582
http_req_receiving.............: avg=58.17µs min=15.36µs med=55.53µs max=299.47µs p(90)=79.87µs p(95)=90.45µs
http_req_sending...............: avg=24.34µs min=10.19µs med=22.62µs max=95.09µs p(90)=32.29µs p(95)=37.01µs
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=938.01µs min=168.91µs med=302.02µs max=216.12ms p(90)=389.59µs p(95)=429.46µs
http_reqs......................: 582 19.360202/s
iteration_duration.............: avg=103.22ms min=100.4ms med=101.07ms max=395.96ms p(90)=101.31ms p(95)=101.49ms
iterations.....................: 582 19.360202/s
vus............................: 2 min=2 max=2
vus_max........................: 2 min=2 max=2

2 changes: 1 addition & 1 deletion pkg/handlers/testdata/k6-output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

execution: local
script: /tmp/k6-script504149289
output: cloud (https://app.k6.io/runs/1157843)
output: cloud (https://somewhere.grafana.net/a/k6-app/runs/1157843)

scenarios: (100.00%) 1 scenario, 2 max VUs, 1m0s max duration (incl. graceful stop):
* default: 2 looping VUs for 30s (gracefulStop: 30s)
Expand Down

0 comments on commit 6ddd79f

Please sign in to comment.