Skip to content

Commit eee1f21

Browse files
committed
fix deployment issues
1 parent 4f2e953 commit eee1f21

25 files changed

+612
-3
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# image used to compile your Swift code
2-
FROM public.ecr.aws/docker/library/swift:6.1-amazonlinux2
2+
FROM public.ecr.aws/docker/library/swift:6.2-amazonlinux2
33
RUN yum -y install git jq tar zip openssl-devel

Examples/quoteapi-alb/Makefile

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
### Add functions here and link them to builder-bot format MUST BE "build-FunctionResourceName in template.yaml"
2+
3+
build-QuoteServiceALB: builder-bot
4+
5+
# Helper commands
6+
build:
7+
sam build
8+
9+
deploy:
10+
sam deploy
11+
12+
logs:
13+
sam logs --stack-name QuoteServiceALB
14+
15+
tail:
16+
sam logs --stack-name QuoteServiceALB --tail
17+
18+
local:
19+
swift run QuoteServiceALB
20+
21+
local-invoke:
22+
sam local invoke QuoteServiceALB --event events/GetQuote.json
23+
24+
###################### No Change required below this line ##########################
25+
26+
builder-bot:
27+
$(eval $@PRODUCT = $(subst build-,,$(MAKECMDGOALS)))
28+
$(eval $@BUILD_DIR = $(PWD)/.aws-sam/build-swift)
29+
$(eval $@STAGE = $($@BUILD_DIR)/lambda)
30+
$(eval $@ARTIFACTS_DIR = $(PWD)/.aws-sam/build/$($@PRODUCT))
31+
32+
## Building from swift-openapi-lambda in a local directory (not from Github)
33+
## 2. Change `Package.swift` dependency to path: "../.."
34+
35+
## 3. add /../.. to BUILD_SRC
36+
$(eval $@BUILD_SRC = $(PWD)/../..)
37+
## $(eval $@BUILD_SRC = $(PWD))
38+
39+
## 4. add `cd Examples/quoteapi-alb &&` to the docker BUILD_CMD
40+
$(eval $@BUILD_CMD = "cd Examples/quoteapi-alb && swift build --static-swift-stdlib --product $($@PRODUCT) -c release --build-path /build-target")
41+
## $(eval $@BUILD_CMD = "swift build --static-swift-stdlib --product $($@PRODUCT) -c release --build-path /build-target")
42+
43+
# build docker image to compile Swift for Linux
44+
docker build -f Dockerfile . -t swift-builder
45+
46+
# prep directories
47+
mkdir -p $($@BUILD_DIR)/lambda $($@ARTIFACTS_DIR)
48+
49+
# compile application inside Docker image using source code from local project folder
50+
51+
docker run --rm -v $($@BUILD_DIR):/build-target -v $($@BUILD_SRC):/build-src -w /build-src swift-builder bash -cl $($@BUILD_CMD)
52+
53+
# create lambda bootstrap file
54+
docker run --rm -v $($@BUILD_DIR):/build-target -v `pwd`:/build-src -w /build-src swift-builder bash -cl "cd /build-target/lambda && ln -s $($@PRODUCT) /bootstrap"
55+
56+
# copy binary to stage
57+
cp $($@BUILD_DIR)/release/$($@PRODUCT) $($@STAGE)/bootstrap
58+
59+
# copy resources to stage (if they exist)
60+
[ -d "$($@BUILD_DIR)/release/$($@PRODUCT)_$($@PRODUCT).resources" ] && cp $($@BUILD_DIR)/release/$($@PRODUCT)_$($@PRODUCT).resources/* $($@STAGE) || true
61+
62+
# copy app from stage to artifacts dir
63+
cp $($@STAGE)/* $($@ARTIFACTS_DIR)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// swift-tools-version: 6.0
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "QuoteServiceALB",
8+
platforms: [
9+
.macOS(.v15)
10+
],
11+
products: [
12+
.executable(name: "QuoteServiceALB", targets: ["QuoteServiceALB"])
13+
],
14+
dependencies: [
15+
.package(url: "https://github.com/apple/swift-openapi-generator.git", from: "1.4.0"),
16+
.package(url: "https://github.com/apple/swift-openapi-runtime.git", from: "1.8.2"),
17+
.package(url: "https://github.com/awslabs/swift-aws-lambda-runtime.git", from: "2.0.0"),
18+
.package(url: "https://github.com/awslabs/swift-aws-lambda-events.git", from: "1.2.0"),
19+
.package(name: "swift-openapi-lambda", path: "../.."),
20+
],
21+
targets: [
22+
.executableTarget(
23+
name: "QuoteServiceALB",
24+
dependencies: [
25+
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"),
26+
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
27+
.product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"),
28+
.product(name: "OpenAPILambda", package: "swift-openapi-lambda"),
29+
],
30+
path: "Sources/QuoteAPI",
31+
resources: [
32+
.copy("openapi.yaml"),
33+
.copy("openapi-generator-config.yaml"),
34+
],
35+
plugins: [
36+
.plugin(
37+
name: "OpenAPIGenerator",
38+
package: "swift-openapi-generator"
39+
)
40+
]
41+
)
42+
]
43+
)

Examples/quoteapi-alb/README.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# QuoteAPI ALB Example
2+
3+
This application illustrates how to deploy a Server-Side Swift workload on AWS using an Application Load Balancer (ALB) with Lambda targets. The workload is a simple REST API that returns stock quotes. Requests to the ALB are forwarded to an AWS Lambda Function written in Swift using the OpenAPI Lambda library.
4+
5+
## Prerequisites
6+
7+
To build this sample application, you need:
8+
9+
- [AWS Account](https://console.aws.amazon.com/)
10+
- [AWS Command Line Interface (AWS CLI)](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html) - install the CLI and [configure](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html) it with credentials to your AWS account
11+
- [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html) - a command-line tool used to create serverless workloads on AWS
12+
- [Docker Desktop](https://www.docker.com/products/docker-desktop/) - to compile your Swift code for Linux deployment to AWS Lambda
13+
14+
## Build the application
15+
16+
The **sam build** command uses Docker to compile your Swift Lambda function and package it for deployment to AWS.
17+
18+
```bash
19+
sam build
20+
```
21+
22+
On macOS, you might need to run this command if `sam` doesn't see `docker`:
23+
```bash
24+
export DOCKER_HOST=unix://$HOME/.docker/run/docker.sock
25+
```
26+
27+
## Deploy the application
28+
29+
The **sam deploy** command creates the Lambda function, Application Load Balancer, and associated VPC resources in your AWS account.
30+
31+
```bash
32+
sam deploy --guided
33+
```
34+
35+
## Use the API
36+
37+
At the end of the deployment, SAM displays the endpoint of your Application Load Balancer:
38+
39+
```bash
40+
Outputs
41+
----------------------------------------------------------------------------------------
42+
Key QuoteAPILoadBalancerURL
43+
Description Application Load Balancer URL for QuoteAPI
44+
Value http://QuoteAPILoadBalancer-123456789.us-east-1.elb.amazonaws.com/stocks/AAPL
45+
----------------------------------------------------------------------------------------
46+
```
47+
48+
Use cURL or a tool such as [Postman](https://www.postman.com/) to interact with your API. Replace **[your-alb-endpoint]** with the QuoteAPILoadBalancerURL value from the deployment output.
49+
50+
**Invoke the API Endpoint**
51+
52+
```bash
53+
curl http://[your-alb-endpoint]/stocks/AMZN
54+
```
55+
56+
## Test the API Locally
57+
58+
SAM also allows you to execute your Lambda functions locally on your development computer.
59+
60+
**Invoke the Lambda Function Locally**
61+
62+
```bash
63+
sam local invoke QuoteServiceALB --event events/GetQuote.json
64+
```
65+
66+
On macOS, you might need to run this command if `sam` doesn't see `docker`:
67+
```bash
68+
export DOCKER_HOST=unix://$HOME/.docker/run/docker.sock
69+
```
70+
71+
## Architecture
72+
73+
This example demonstrates:
74+
75+
- **Application Load Balancer**: Routes HTTP requests to Lambda functions
76+
- **Lambda Target Group**: Configures the ALB to forward requests to Lambda
77+
- **VPC Setup**: Creates a VPC with public subnets for the ALB
78+
- **Security Groups**: Controls inbound traffic to the ALB
79+
- **OpenAPI Integration**: Uses Swift OpenAPI Lambda library with ALB events
80+
81+
## Cleanup
82+
83+
When finished with your application, use SAM to delete it from your AWS account. Answer **Yes (y)** to all prompts. This will delete all of the application resources created in your AWS account.
84+
85+
```bash
86+
sam delete
87+
```
88+
89+
> **⚠️ Security and Reliability Notice**
90+
>
91+
> This is an example application for demonstration purposes. When deploying such infrastructure in production environments, we strongly encourage you to follow these best practices for improved security and resiliency:
92+
>
93+
> - Enable access logging on Application Load Balancer ([documentation](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html))
94+
> - Ensure that AWS Lambda function is configured for function-level concurrent execution limit ([concurrency documentation](https://docs.aws.amazon.com/lambda/latest/dg/lambda-concurrency.html), [configuration guide](https://docs.aws.amazon.com/lambda/latest/dg/configuration-concurrency.html))
95+
> - Check encryption settings for Lambda environment variables ([documentation](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars-encryption.html))
96+
> - Ensure that AWS Lambda function is configured for a Dead Letter Queue (DLQ) ([documentation](https://docs.aws.amazon.com/lambda/latest/dg/invocation-async-retain-records.html#invocation-dlq))
97+
> - Configure HTTPS/TLS termination on the Application Load Balancer ([documentation](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html))
98+
>
99+
> **Note:** The `openapi.yaml` file in this example is not suited for production. In real-world scenarios, you must:
100+
> 1. Ensure that the global security field has rules defined
101+
> 2. Ensure that security operations is not empty ([OpenAPI Security Specification](https://learn.openapis.org/specification/security.html))
102+
> 3. Follow proper authentication, authorization, input validation, and error handling practices
103+
>
104+
> As per Checkov CKV_OPENAPI_4 and CKV_OPENAPI_5 security checks.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift OpenAPI Lambda open source project
4+
//
5+
// Copyright (c) 2023 Amazon.com, Inc. or its affiliates
6+
// and the Swift OpenAPI Lambda project authors
7+
// Licensed under Apache License v2.0
8+
//
9+
// See LICENSE.txt for license information
10+
// See CONTRIBUTORS.txt for the list of Swift OpenAPI Lambda project authors
11+
//
12+
// SPDX-License-Identifier: Apache-2.0
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
import Foundation
17+
import Logging
18+
import OpenAPIRuntime
19+
import OpenAPILambda
20+
21+
@main
22+
struct QuoteServiceALBImpl: APIProtocol, OpenAPILambdaALB {
23+
24+
let logger: Logger
25+
26+
func register(transport: OpenAPILambdaTransport) throws {
27+
28+
// OPTIONAL
29+
// you have a chance here to customize the routes, for example
30+
try transport.router.get("/health") { _, _ in
31+
"OK"
32+
}
33+
logger.trace("Available Routes\n\(transport.router)") // print the router tree (for debugging purposes)
34+
35+
// OPTIONAL
36+
// to log all requests and their responses, add a logging middleware
37+
let loggingMiddleware = LoggingMiddleware(logger: logger)
38+
39+
// MANDATORY (middlewares are optional)
40+
try self.registerHandlers(on: transport, middlewares: [loggingMiddleware])
41+
}
42+
43+
static func main() async throws {
44+
let openAPIService = QuoteServiceALBImpl()
45+
try await openAPIService.run()
46+
}
47+
48+
init() {
49+
var logger = Logger(label: "QuoteServiceALB")
50+
logger.logLevel = .trace
51+
self.logger = logger
52+
}
53+
54+
func getQuote(_ input: Operations.getQuote.Input) async throws -> Operations.getQuote.Output {
55+
logger.trace("GetQuote - Started")
56+
57+
let symbol = input.path.symbol
58+
59+
var date: Date = Date()
60+
if let dateString = input.query.date {
61+
let dateFormatter = DateFormatter()
62+
dateFormatter.dateFormat = "yyyyMMdd"
63+
date = dateFormatter.date(from: dateString) ?? Date()
64+
}
65+
66+
let price = Components.Schemas.quote(
67+
symbol: symbol,
68+
price: Double.random(in: 100..<150).rounded(),
69+
change: Double.random(in: -5..<5).rounded(),
70+
changePercent: Double.random(in: -0.05..<0.05),
71+
volume: Double.random(in: 10000..<100000).rounded(),
72+
timestamp: date
73+
)
74+
75+
logger.trace("GetQuote - Returning")
76+
77+
return .ok(.init(body: .json(price)))
78+
}
79+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
generate:
2+
- types
3+
- server
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
openapi: 3.1.0
2+
info:
3+
title: StockQuoteService
4+
version: 1.0.0
5+
6+
components:
7+
schemas:
8+
quote:
9+
type: object
10+
properties:
11+
symbol:
12+
type: string
13+
price:
14+
type: number
15+
change:
16+
type: number
17+
changePercent:
18+
type: number
19+
volume:
20+
type: number
21+
timestamp:
22+
type: string
23+
format: date-time
24+
25+
paths:
26+
/stocks/{symbol}:
27+
get:
28+
summary: Get the latest quote for a stock
29+
operationId: getQuote
30+
parameters:
31+
- name: symbol
32+
in: path
33+
required: true
34+
schema:
35+
type: string
36+
- name: date
37+
in: query
38+
required: false
39+
schema:
40+
type: string
41+
format: date
42+
tags:
43+
- stocks
44+
responses:
45+
200:
46+
description: OK
47+
content:
48+
application/json:
49+
schema:
50+
$ref: '#/components/schemas/quote'
51+
400:
52+
description: Bad Request
53+
404:
54+
description: Not Found
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"requestContext": {
3+
"elb": {
4+
"targetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/lambda-target/50dc6c495c0c9188"
5+
}
6+
},
7+
"httpMethod": "GET",
8+
"path": "/stocks/AAPL",
9+
"queryStringParameters": {},
10+
"headers": {
11+
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
12+
"accept-encoding": "gzip, deflate",
13+
"accept-language": "en-US,en;q=0.9",
14+
"connection": "keep-alive",
15+
"host": "lambda-alb-123578498.us-east-1.elb.amazonaws.com",
16+
"upgrade-insecure-requests": "1",
17+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
18+
"x-amzn-trace-id": "Root=1-5c536348-3d683b8b04734faae651f476",
19+
"x-forwarded-for": "72.12.164.125",
20+
"x-forwarded-port": "80",
21+
"x-forwarded-proto": "http"
22+
},
23+
"body": "",
24+
"isBase64Encoded": false
25+
}

0 commit comments

Comments
 (0)