Skip to content

Commit

Permalink
feat: add new pattern "Request-Driven Web Service" to init AWS App Ru…
Browse files Browse the repository at this point in the history
…nner services (aws#2340)


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
  • Loading branch information
efekarakus authored and thrau committed Dec 9, 2022
1 parent 91a7045 commit 5f8077a
Show file tree
Hide file tree
Showing 113 changed files with 7,341 additions and 968 deletions.
9 changes: 9 additions & 0 deletions .release/buildspec_e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ batch:
variables:
TEST_SUITE: exec
APP_REGION: ap-northeast-1
- identifier: apprunner
env:
privileged-mode: true
type: LINUX_CONTAINER
compute-type: BUILD_GENERAL1_LARGE
image: aws/codebuild/standard:5.0
variables:
TEST_SUITE: apprunner
APP_REGION: ap-northeast-1


phases:
Expand Down
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ gen-mocks: tools
${GOBIN}/mockgen -source=./internal/pkg/cli/completion.go -package=mocks -destination=./internal/pkg/cli/mocks/mock_completion.go
${GOBIN}/mockgen -source=./internal/pkg/cli/identity.go -package=mocks -destination=./internal/pkg/cli/mocks/mock_identity.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_lb_web_service.go -source=./internal/pkg/describe/lb_web_service.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_rd_web_service.go -source=./internal/pkg/describe/rd_web_service.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_backend_service.go -source=./internal/pkg/describe/backend_service.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_service.go -source=./internal/pkg/describe/service.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_describe.go -source=./internal/pkg/describe/describe.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_stack.go -source=./internal/pkg/describe/stack.go
Expand Down Expand Up @@ -179,6 +181,7 @@ gen-mocks: tools
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/deploy/cloudformation/mocks/mock_cloudformation.go -source=./internal/pkg/deploy/cloudformation/cloudformation.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/deploy/cloudformation/stack/mocks/mock_env.go -source=./internal/pkg/deploy/cloudformation/stack/env.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/deploy/cloudformation/stack/mocks/mock_lb_web_svc.go -source=./internal/pkg/deploy/cloudformation/stack/lb_web_svc.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/deploy/cloudformation/stack/mocks/mock_rd_web_svc.go -source=./internal/pkg/deploy/cloudformation/stack/rd_web_svc.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/deploy/cloudformation/stack/mocks/mock_backend_svc.go -source=./internal/pkg/deploy/cloudformation/stack/backend_svc.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/deploy/cloudformation/stack/mocks/mock_scheduled_job.go -source=./internal/pkg/deploy/cloudformation/stack/scheduled_job.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/template/mocks/mock_template.go -source=./internal/pkg/template/template.go
Expand All @@ -190,5 +193,4 @@ gen-mocks: tools
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/initialize/mocks/mock_workload.go -source=./internal/pkg/initialize/workload.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/ecs/mocks/mock_ecs.go -source=./internal/pkg/ecs/ecs.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/ecs/mocks/mock_run_task_request.go -source=./internal/pkg/ecs/run_task_request.go


${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/aws/apprunner/mocks/mock_apprunner.go -source=./internal/pkg/aws/apprunner/apprunner.go
59 changes: 59 additions & 0 deletions e2e/apprunner/apprunner_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package apprunner_test

import (
"fmt"
"testing"
"time"

"github.com/aws/copilot-cli/e2e/internal/client"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var cli *client.CLI
var appName string

const svcName = "front-end"
const envName = "test"

/**
The Init Suite runs through the copilot init workflow for a brand new
application. It creates a single environment, deploys a service to it, and then
tears it down.
*/
func TestInit(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "App Runner Suite")
}

var _ = BeforeSuite(func() {
var err error
cli, err = client.NewCLI()
Expect(err).NotTo(HaveOccurred())
Expect(err).NotTo(HaveOccurred())
appName = fmt.Sprintf("e2e-apprunner-%d", time.Now().Unix())
})

var _ = AfterSuite(func() {
_, err := cli.SvcDelete(svcName)
Expect(err).NotTo(HaveOccurred())

_, err = cli.EnvDelete(envName)
Expect(err).NotTo(HaveOccurred())

_, err = cli.AppDelete()
Expect(err).NotTo(HaveOccurred())
})

func BeforeAll(fn func()) {
first := true
BeforeEach(func() {
if first {
fn()
first = false
}
})
}
269 changes: 269 additions & 0 deletions e2e/apprunner/apprunner_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package apprunner_test

import (
"fmt"
"net/http"

"github.com/aws/copilot-cli/e2e/internal/client"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

type countAssertionTracker struct {
expected int
actual int
}

var _ = Describe("App Runner", func() {

var (
initErr error
)

BeforeAll(func() {
_, initErr = cli.Init(&client.InitRequest{
AppName: appName,
WorkloadName: svcName,
ImageTag: "gallopinggurdey",
Dockerfile: "./front-end/Dockerfile",
WorkloadType: "Request-Driven Web Service",
Deploy: true,
SvcPort: "80",
})
})

Context("run init with app runner", func() {
It("init does not return an error", func() {
Expect(initErr).NotTo(HaveOccurred())
})
})

Context("run svc ls to ensure the service was created", func() {
var (
svcList *client.SvcListOutput
svcListError error
)

BeforeAll(func() {
svcList, svcListError = cli.SvcList(appName)
})

It("should not return an error", func() {
Expect(svcListError).NotTo(HaveOccurred())
})

It("should return one service", func() {
Expect(len(svcList.Services)).To(Equal(1))
Expect(svcList.Services[0].Name).To(Equal(svcName))
Expect(svcList.Services[0].AppName).To(Equal(appName))
Expect(svcList.Services[0].Type).To(Equal("Request-Driven Web Service"))
})
})

Context("run svc status to ensure that the service is healthy", func() {
var (
svcStatus *client.SvcStatusOutput
svcStatusError error
)

BeforeAll(func() {
svcStatus, svcStatusError = cli.SvcStatus(&client.SvcStatusRequest{
Name: svcName,
AppName: appName,
EnvName: envName,
})
})

It("should not return an error", func() {
Expect(svcStatusError).NotTo(HaveOccurred())
})

It("should return app runner service status", func() {
Expect(svcStatus.Service.Status).To(Equal("RUNNING"))
})
})

Context("run storage init and svc deploy to create an S3 bucket", func() {
var (
storageInitErr error
deployErr error
)

BeforeAll(func() {
_, storageInitErr = cli.StorageInit(&client.StorageInitRequest{
StorageName: "s3storage",
StorageType: "S3",
WorkloadName: svcName,
})
_, deployErr = cli.SvcDeploy(&client.SvcDeployInput{
EnvName: envName,
Name: svcName,
})
})

It("should not return an error", func() {
Expect(storageInitErr).NotTo(HaveOccurred())
Expect(deployErr).NotTo(HaveOccurred())
})
})

Context("run svc show to retrieve the service configuration, resources, and endpoint, then query the service", func() {
var (
svc *client.SvcShowOutput
svcShowError error
)

BeforeAll(func() {
svc, svcShowError = cli.SvcShow(&client.SvcShowRequest{
Name: svcName,
AppName: appName,
Resources: true,
})
})

It("should not return an error", func() {
Expect(svcShowError).NotTo(HaveOccurred())
})

It("should return correct configuration", func() {
Expect(svc.SvcName).To(Equal(svcName))
Expect(svc.AppName).To(Equal(appName))
Expect(len(svc.Configs)).To(Equal(1))
Expect(svc.Configs[0].Environment).To(Equal(envName))
Expect(svc.Configs[0].CPU).To(Equal("1024"))
Expect(svc.Configs[0].Memory).To(Equal("2048"))
Expect(svc.Configs[0].Port).To(Equal("80"))
})

It("should return correct environment variables", func() {
fmt.Printf("\n\nenvironment variables: %+v\n\n", svc.Variables)
Expect(len(svc.Variables)).To(Equal(4))
expectedVars := map[string]string{
"COPILOT_APPLICATION_NAME": appName,
"COPILOT_ENVIRONMENT_NAME": envName,
"COPILOT_SERVICE_NAME": svcName,
"S3STORAGE_NAME": fmt.Sprintf("%s-%s-%s-s3storage", appName, envName, svcName),
}
for _, variable := range svc.Variables {
Expect(variable.Value).To(Equal(expectedVars[variable.Name]))
}
})

It("should return the correct resources", func() {
Expect(len(svc.Resources)).To(Equal(1))
Expect(svc.Resources[envName]).NotTo(BeNil())
Expect(len(svc.Resources[envName])).To(Equal(4))
expectedTypes := map[string]*countAssertionTracker{
"AWS::IAM::Role": {2, 0},
"AWS::CloudFormation::Stack": {1, 0},
"AWS::AppRunner::Service": {1, 0},
}

for _, r := range svc.Resources[envName] {
if expectedTypes[r.Type] == nil {
expectedTypes[r.Type] = &countAssertionTracker{0, 0}
}
expectedTypes[r.Type].actual++
}

for t, v := range expectedTypes {
Expect(v.actual).To(
Equal(v.expected),
fmt.Sprintf("Expected %v resources of type %v, received %v", v.expected, t, v.actual))
}
})

It("should return svc endpoints", func() {
Expect(len(svc.Routes)).To(Equal(1))
Expect(svc.Routes[0].Environment).To(Equal(envName))
Expect(svc.Routes[0].URL).NotTo(BeEmpty())
Eventually(func() (int, error) {
resp, fetchErr := http.Get(svc.Routes[0].URL)
return resp.StatusCode, fetchErr
}, "30s", "1s").Should(Equal(200))
})
})

Context("run svc logs to troubleshoot", func() {
var (
svcLogs []client.SvcLogsOutput
svcLogsErr error
)

BeforeAll(func() {
svcLogs, svcLogsErr = cli.SvcLogs(&client.SvcLogsRequest{
AppName: appName,
Name: svcName,
EnvName: "test",
Since: "1h",
})
})

It("should not return an error", func() {
Expect(svcLogsErr).NotTo(HaveOccurred())
})

It("should return valid log lines", func() {
Expect(len(svcLogs)).To(BeNumerically(">", 0))
for _, logLine := range svcLogs {
Expect(logLine.Message).NotTo(Equal(""))
Expect(logLine.LogStreamName).NotTo(Equal(""))
Expect(logLine.Timestamp).NotTo(Equal(0))
Expect(logLine.IngestionTime).NotTo(Equal(0))
}
})
})

Context("run pause and then resume the service", func() {
var (
svcPauseError error
pausedSvcStatus *client.SvcStatusOutput
pausedSvcStatusError error
svcResumeError error
resumedSvcStatus *client.SvcStatusOutput
resumedSvcStatusError error
)

BeforeAll(func() {
_, svcPauseError = cli.SvcPause(&client.SvcPauseRequest{
AppName: appName,
EnvName: envName,
Name: svcName,
})
pausedSvcStatus, pausedSvcStatusError = cli.SvcStatus(&client.SvcStatusRequest{
Name: svcName,
AppName: appName,
EnvName: envName,
})
_, svcResumeError = cli.SvcResume(&client.SvcResumeRequest{
AppName: appName,
EnvName: envName,
Name: svcName,
})
resumedSvcStatus, resumedSvcStatusError = cli.SvcStatus(&client.SvcStatusRequest{
Name: svcName,
AppName: appName,
EnvName: envName,
})
})

It("should not an return error", func() {
Expect(svcPauseError).NotTo(HaveOccurred())
Expect(pausedSvcStatusError).NotTo(HaveOccurred())
Expect(svcResumeError).NotTo(HaveOccurred())
Expect(resumedSvcStatusError).NotTo(HaveOccurred())
})

It("should successfully pause service", func() {
Expect(pausedSvcStatus.Service.Status).To(Equal("PAUSED"))
})

It("should successfully resume service", func() {
Expect(resumedSvcStatus.Service.Status).To(Equal("RUNNING"))
})
})

})
4 changes: 4 additions & 0 deletions e2e/apprunner/front-end/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM nginx

RUN mkdir -p /www/data/
COPY index.html /www/data/
14 changes: 14 additions & 0 deletions e2e/apprunner/front-end/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>AWS Copilot CLI</title>
</head>
<body style="background-color:rgb(35, 47, 62);">
<div style='vertical-align: middle; margin:auto; width:50%; padding-top:200px; text-align: center;'>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="141px" height="171px" viewBox="-0.5 -0.5 81 101"><defs><linearGradient x1="0%" y1="0%" x2="0%" y2="100%" id="mx-gradient-232f3e-100-232f3e-100-s-0"><stop offset="0%" style="stop-color:#232F3E"/><stop offset="100%" style="stop-color:#232F3E"/></linearGradient></defs><g><path d="M 0 0 L 80 0 L 80 100 L 0 100 Z" fill="#ffffff" stroke="none" pointer-events="all"/><path d="M 1 1 L 79 1 L 79 79 L 1 79 Z" fill="url(#mx-gradient-232f3e-100-232f3e-100-s-0)" stroke="none" pointer-events="all"/><path d="M 38.07 13.01 C 37.9 13.01 37.73 13.06 37.57 13.15 L 16.61 25.24 C 16.28 25.43 16.08 25.79 16.08 26.17 L 16.08 52.54 C 16.09 52.92 16.29 53.26 16.61 53.45 L 39.42 66.81 C 39.75 67.01 40.16 67.01 40.49 66.81 L 61.42 54.63 C 61.75 54.44 61.95 54.09 61.95 53.71 C 61.95 53.33 61.74 52.98 61.41 52.79 L 51.36 47.12 C 51.03 46.94 50.63 46.94 50.31 47.13 L 40.07 53.14 L 28.12 46.26 L 28.12 32.5 L 38.63 26.49 C 38.96 26.3 39.16 25.95 39.16 25.57 L 39.16 14.07 C 39.16 13.78 39.04 13.51 38.84 13.31 C 38.64 13.11 38.36 13 38.07 13.01 Z M 42.08 13.05 C 41.79 13.04 41.51 13.15 41.31 13.35 C 41.1 13.55 40.98 13.82 40.98 14.11 L 40.98 25.62 C 40.99 26 41.19 26.35 41.52 26.54 L 51.84 32.55 L 51.84 44.59 C 51.84 44.96 52.04 45.31 52.37 45.5 L 62.32 51.28 C 62.65 51.47 63.05 51.47 63.38 51.28 C 63.71 51.09 63.92 50.74 63.92 50.36 L 63.92 26.19 C 63.91 25.81 63.71 25.46 63.38 25.28 L 42.58 13.2 C 42.43 13.11 42.25 13.06 42.08 13.05 Z M 37.05 15.91 L 37.05 24.96 L 26.54 30.97 C 26.21 31.16 26 31.51 26 31.89 L 26 46.87 C 26 47.25 26.2 47.6 26.54 47.79 L 39.54 55.28 C 39.87 55.47 40.28 55.47 40.6 55.28 L 50.85 49.27 L 58.76 53.73 L 39.96 64.67 L 18.2 51.92 L 18.2 26.77 Z M 43.11 15.95 L 61.8 26.8 L 61.8 48.52 L 53.96 43.97 L 53.96 31.94 C 53.96 31.56 53.76 31.21 53.43 31.02 L 43.11 25.01 Z" fill="#ffffff" stroke="none" pointer-events="all"/><g transform="translate(8.5,84.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="62" height="10" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; vertical-align: top; width: 63px; white-space: nowrap; overflow-wrap: normal; font-weight: bold; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;">Amazon ECS</div></div></foreignObject><text x="31" y="10" fill="#232F3E" text-anchor="middle" font-size="10px" font-family="Helvetica" font-weight="bold">Amazon ECS</text></switch></g></g></svg>
</div>
</body>
</html>
Loading

0 comments on commit 5f8077a

Please sign in to comment.