Skip to content

Commit 4efabf4

Browse files
authored
test(e2e): add worker service test suite (#2879)
The test ensures: 1. The worker service receives and consumes sqs messages 2. Can contact other services via service discovery By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the Apache 2.0 License.
1 parent 37b569f commit 4efabf4

File tree

11 files changed

+346
-6
lines changed

11 files changed

+346
-6
lines changed

.release/buildspec_e2e.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,15 @@ batch:
120120
APP_REGION: eu-west-1
121121
TESTENV_REGION: eu-west-1
122122
PRODENV_REGION: eu-central-1
123-
123+
- identifier: worker
124+
env:
125+
privileged-mode: true
126+
type: LINUX_CONTAINER
127+
compute-type: BUILD_GENERAL1_LARGE
128+
image: aws/codebuild/standard:5.0
129+
variables:
130+
TEST_SUITE: worker
131+
APP_REGION: eu-central-1
124132

125133
phases:
126134
install:

e2e/internal/client/cli.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,12 @@ type EnvShowRequest struct {
8080

8181
// SvcInitRequest contains the parameters for calling copilot svc init.
8282
type SvcInitRequest struct {
83-
Name string
84-
SvcType string
85-
Dockerfile string
86-
Image string
87-
SvcPort string
83+
Name string
84+
SvcType string
85+
Dockerfile string
86+
Image string
87+
SvcPort string
88+
TopicSubscriptions []string
8889
}
8990

9091
// SvcShowRequest contains the parameters for calling copilot svc show.
@@ -314,6 +315,9 @@ func (cli *CLI) SvcInit(opts *SvcInitRequest) (string, error) {
314315
if opts.Image != "" {
315316
args = append(args, "--image", opts.Image)
316317
}
318+
if len(opts.TopicSubscriptions) > 0 {
319+
args = append(args, "--subscribe-topics", strings.Join(opts.TopicSubscriptions, ","))
320+
}
317321
return cli.exec(
318322
exec.Command(cli.path, args...))
319323
}

e2e/worker/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Worker Service E2E tests
2+
3+
The goal of the `worker` e2e test is to validate the most common usecases for a
4+
[Worker Service](https://aws.github.io/copilot-cli/docs/concepts/services/#worker-service).
5+
6+
- Receiving and deleting messages from the default SQS queue: `COPILOT_QUEUE_URI`
7+
- Making requests via service discovery to other services in the application.

e2e/worker/frontend/Dockerfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FROM node:14-slim
2+
WORKDIR /usr/src/app
3+
COPY package*.json ./
4+
RUN npm install
5+
COPY . .
6+
EXPOSE 8080
7+
CMD [ "node", "server.js" ]

e2e/worker/frontend/package.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "frontend",
3+
"version": "1.0.0",
4+
"author": "The ECS DevX team",
5+
"main": "server.js",
6+
"scripts": {
7+
"start": "node server.js"
8+
},
9+
"dependencies": {
10+
"@aws-sdk/client-sns": "^3.28.0",
11+
"express": "^4.16.1"
12+
}
13+
}

e2e/worker/frontend/server.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
'use strict';
5+
6+
const express = require('express');
7+
const { SNSClient, PublishCommand } = require("@aws-sdk/client-sns");
8+
9+
const PORT = 8080;
10+
const HOST = '0.0.0.0';
11+
const client = new SNSClient({ region: process.env.AWS_DEFAULT_REGION });
12+
const app = express();
13+
14+
// Start the service waiting for an ack message.
15+
let status = 'waiting on acknowledgement';
16+
17+
// Each health check request from the ALB will result in publishing an event.
18+
app.get('/', async (req, res) => {
19+
const {events} = JSON.parse(process.env.COPILOT_SNS_TOPIC_ARNS);
20+
const out = await client.send(new PublishCommand({
21+
Message: "healthcheck",
22+
TopicArn: events,
23+
}));
24+
console.log(JSON.stringify(out));
25+
res.send('hello');
26+
});
27+
28+
app.get('/status', (req, res) => {
29+
res.send(status);
30+
});
31+
32+
app.post('/ack', async (req, res) => {
33+
status = 'consumed';
34+
res.send('ok');
35+
});
36+
37+
app.listen(PORT, HOST);
38+
console.log(`Running on http://${HOST}:${PORT}`);

e2e/worker/worker/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FROM node:14-slim
2+
WORKDIR /usr/src/app
3+
COPY package*.json ./
4+
RUN npm install
5+
COPY . .
6+
CMD [ "node", "index.js" ]

e2e/worker/worker/index.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
const { SQSClient, ReceiveMessageCommand, DeleteMessageCommand } = require("@aws-sdk/client-sqs");
4+
const axios = require("axios");
5+
const client = new SQSClient({ region: process.env.AWS_DEFAULT_REGION });
6+
7+
console.log(`COPILOT_QUEUE_URI: ${process.env.COPILOT_QUEUE_URI}`);
8+
9+
const eventsQueue = process.env.COPILOT_QUEUE_URI;
10+
11+
function sleep(ms) {
12+
return new Promise(resolve => setTimeout(resolve, ms));
13+
}
14+
15+
(async () => {
16+
console.log(`The queue url created is: ${eventsQueue}`);
17+
while(true) {
18+
try {
19+
await sleep(300);
20+
const out = await client.send(new ReceiveMessageCommand({
21+
QueueUrl: eventsQueue,
22+
WaitTimeSeconds: 10,
23+
}));
24+
console.log(`results: ${JSON.stringify(out)}`);
25+
26+
if (out.Messages === undefined || out.Messages.length === 0) {
27+
continue;
28+
}
29+
30+
const resp = await axios.post(`http://frontend.${process.env.COPILOT_SERVICE_DISCOVERY_ENDPOINT}:8080/ack`);
31+
console.log(`response from frontend service: ${JSON.stringify(resp)}`);
32+
33+
await client.send( new DeleteMessageCommand({
34+
QueueUrl: eventsQueue,
35+
ReceiptHandle: out.Messages[0].ReceiptHandle,
36+
}));
37+
} catch (err) {
38+
console.error(err);
39+
}
40+
}
41+
})();

e2e/worker/worker/package.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "worker",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"dependencies": {
10+
"@aws-sdk/client-sqs": "^3.27.0",
11+
"axios": "^0.21.1"
12+
}
13+
}

e2e/worker/worker_suite_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package worker_test
5+
6+
import (
7+
"fmt"
8+
"testing"
9+
"time"
10+
11+
"github.com/aws/copilot-cli/e2e/internal/client"
12+
. "github.com/onsi/ginkgo"
13+
. "github.com/onsi/gomega"
14+
)
15+
16+
var (
17+
cli *client.CLI
18+
appName string
19+
)
20+
21+
const (
22+
lbwsServiceName = "frontend"
23+
workerServiceName = "worker"
24+
25+
envName = "test"
26+
)
27+
28+
func TestWorker(t *testing.T) {
29+
RegisterFailHandler(Fail)
30+
RunSpecs(t, "App Runner Suite")
31+
}
32+
33+
var _ = BeforeSuite(func() {
34+
var err error
35+
cli, err = client.NewCLI()
36+
Expect(err).NotTo(HaveOccurred())
37+
appName = fmt.Sprintf("e2e-worker-%d", time.Now().Unix())
38+
})
39+
40+
var _ = AfterSuite(func() {
41+
_, err := cli.AppDelete()
42+
Expect(err).NotTo(HaveOccurred())
43+
})

0 commit comments

Comments
 (0)