Skip to content

Commit d86f1af

Browse files
Application Load Balancer (ALB) (#2318)
1 parent 2efa321 commit d86f1af

File tree

51 files changed

+5267
-13
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+5267
-13
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"Projects": [
3+
{
4+
"Name": "Amazon.Lambda.Annotations",
5+
"Type": "Minor",
6+
"ChangelogMessages": [
7+
"Added [ALBApi] attribute for configuring Lambda functions as targets behind an Application Load Balancer"
8+
]
9+
}
10+
]
11+
}

Libraries/Amazon.Lambda.Annotations.slnf

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
"test\\TestCustomAuthorizerApp.IntegrationTests\\TestCustomAuthorizerApp.IntegrationTests.csproj",
1717
"test\\TestServerlessApp.IntegrationTests\\TestServerlessApp.IntegrationTests.csproj",
1818
"test\\TestServerlessApp.NET8\\TestServerlessApp.NET8.csproj",
19-
"test\\TestServerlessApp\\TestServerlessApp.csproj"
19+
"src\\Amazon.Lambda.ApplicationLoadBalancerEvents\\Amazon.Lambda.ApplicationLoadBalancerEvents.csproj",
20+
"test\\TestServerlessApp\\TestServerlessApp.csproj",
21+
"test\\TestServerlessApp.ALB\\TestServerlessApp.ALB.csproj",
22+
"test\\TestServerlessApp.ALB.IntegrationTests\\TestServerlessApp.ALB.IntegrationTests.csproj"
2023
]
2124
}
2225
}

Libraries/Libraries.sln

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestCustomAuthorizerApp.Int
151151
EndProject
152152
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestCustomAuthorizerApp", "test\TestCustomAuthorizerApp\TestCustomAuthorizerApp.csproj", "{3BFA4B73-BA61-4578-833B-C5B3A16EDA9E}"
153153
EndProject
154+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestServerlessApp.ALB", "test\TestServerlessApp.ALB\TestServerlessApp.ALB.csproj", "{8F7C617D-C611-4DC6-A07C-033F13C1835D}"
155+
EndProject
156+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestServerlessApp.ALB.IntegrationTests", "test\TestServerlessApp.ALB.IntegrationTests\TestServerlessApp.ALB.IntegrationTests.csproj", "{80594C21-C6EB-469E-83CC-68F9F661CA5E}"
157+
EndProject
154158
Global
155159
GlobalSection(SolutionConfigurationPlatforms) = preSolution
156160
Debug|Any CPU = Debug|Any CPU
@@ -941,6 +945,30 @@ Global
941945
{3BFA4B73-BA61-4578-833B-C5B3A16EDA9E}.Release|x64.Build.0 = Release|Any CPU
942946
{3BFA4B73-BA61-4578-833B-C5B3A16EDA9E}.Release|x86.ActiveCfg = Release|Any CPU
943947
{3BFA4B73-BA61-4578-833B-C5B3A16EDA9E}.Release|x86.Build.0 = Release|Any CPU
948+
{8F7C617D-C611-4DC6-A07C-033F13C1835D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
949+
{8F7C617D-C611-4DC6-A07C-033F13C1835D}.Debug|Any CPU.Build.0 = Debug|Any CPU
950+
{8F7C617D-C611-4DC6-A07C-033F13C1835D}.Debug|x64.ActiveCfg = Debug|Any CPU
951+
{8F7C617D-C611-4DC6-A07C-033F13C1835D}.Debug|x64.Build.0 = Debug|Any CPU
952+
{8F7C617D-C611-4DC6-A07C-033F13C1835D}.Debug|x86.ActiveCfg = Debug|Any CPU
953+
{8F7C617D-C611-4DC6-A07C-033F13C1835D}.Debug|x86.Build.0 = Debug|Any CPU
954+
{8F7C617D-C611-4DC6-A07C-033F13C1835D}.Release|Any CPU.ActiveCfg = Release|Any CPU
955+
{8F7C617D-C611-4DC6-A07C-033F13C1835D}.Release|Any CPU.Build.0 = Release|Any CPU
956+
{8F7C617D-C611-4DC6-A07C-033F13C1835D}.Release|x64.ActiveCfg = Release|Any CPU
957+
{8F7C617D-C611-4DC6-A07C-033F13C1835D}.Release|x64.Build.0 = Release|Any CPU
958+
{8F7C617D-C611-4DC6-A07C-033F13C1835D}.Release|x86.ActiveCfg = Release|Any CPU
959+
{8F7C617D-C611-4DC6-A07C-033F13C1835D}.Release|x86.Build.0 = Release|Any CPU
960+
{80594C21-C6EB-469E-83CC-68F9F661CA5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
961+
{80594C21-C6EB-469E-83CC-68F9F661CA5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
962+
{80594C21-C6EB-469E-83CC-68F9F661CA5E}.Debug|x64.ActiveCfg = Debug|Any CPU
963+
{80594C21-C6EB-469E-83CC-68F9F661CA5E}.Debug|x64.Build.0 = Debug|Any CPU
964+
{80594C21-C6EB-469E-83CC-68F9F661CA5E}.Debug|x86.ActiveCfg = Debug|Any CPU
965+
{80594C21-C6EB-469E-83CC-68F9F661CA5E}.Debug|x86.Build.0 = Debug|Any CPU
966+
{80594C21-C6EB-469E-83CC-68F9F661CA5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
967+
{80594C21-C6EB-469E-83CC-68F9F661CA5E}.Release|Any CPU.Build.0 = Release|Any CPU
968+
{80594C21-C6EB-469E-83CC-68F9F661CA5E}.Release|x64.ActiveCfg = Release|Any CPU
969+
{80594C21-C6EB-469E-83CC-68F9F661CA5E}.Release|x64.Build.0 = Release|Any CPU
970+
{80594C21-C6EB-469E-83CC-68F9F661CA5E}.Release|x86.ActiveCfg = Release|Any CPU
971+
{80594C21-C6EB-469E-83CC-68F9F661CA5E}.Release|x86.Build.0 = Release|Any CPU
944972
EndGlobalSection
945973
GlobalSection(SolutionProperties) = preSolution
946974
HideSolutionNode = FALSE
@@ -1015,6 +1043,8 @@ Global
10151043
{8D03BDF3-7078-4B46-A3F1-C73BE6D6CE0D} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
10161044
{8EEDD576-7FC4-4FAC-A5A2-F58562753A53} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
10171045
{3BFA4B73-BA61-4578-833B-C5B3A16EDA9E} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
1046+
{8F7C617D-C611-4DC6-A07C-033F13C1835D} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
1047+
{80594C21-C6EB-469E-83CC-68F9F661CA5E} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
10181048
EndGlobalSection
10191049
GlobalSection(ExtensibilityGlobals) = postSolution
10201050
SolutionGuid = {503678A4-B8D1-4486-8915-405A3E9CF0EB}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Unshipped.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,7 @@ AWSLambda0128 | AWSLambdaCSharpGenerator | Error | Authorizer Payload Version Mi
1616
AWSLambda0129 | AWSLambdaCSharpGenerator | Error | Missing LambdaFunction Attribute
1717
AWSLambda0130 | AWSLambdaCSharpGenerator | Error | Invalid return type IAuthorizerResult
1818
AWSLambda0131 | AWSLambdaCSharpGenerator | Error | FromBody not supported on Authorizer functions
19+
AWSLambda0132 | AWSLambdaCSharpGenerator | Error | Invalid ALBApiAttribute
20+
AWSLambda0133 | AWSLambdaCSharpGenerator | Error | ALB Listener Reference Not Found
21+
AWSLambda0134 | AWSLambdaCSharpGenerator | Error | FromRoute not supported on ALB functions
22+
AWSLambda0135 | AWSLambdaCSharpGenerator | Error | Unmapped parameter on ALB function

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,5 +242,37 @@ public static class DiagnosticDescriptors
242242
category: "AWSLambdaCSharpGenerator",
243243
DiagnosticSeverity.Error,
244244
isEnabledByDefault: true);
245+
246+
public static readonly DiagnosticDescriptor InvalidAlbApiAttribute = new DiagnosticDescriptor(
247+
id: "AWSLambda0132",
248+
title: "Invalid ALBApiAttribute",
249+
messageFormat: "Invalid ALBApiAttribute encountered: {0}",
250+
category: "AWSLambdaCSharpGenerator",
251+
DiagnosticSeverity.Error,
252+
isEnabledByDefault: true);
253+
254+
public static readonly DiagnosticDescriptor AlbListenerReferenceNotFound = new DiagnosticDescriptor(
255+
id: "AWSLambda0133",
256+
title: "ALB Listener Reference Not Found",
257+
messageFormat: "The ALBApi ListenerArn references '@{0}', but no resource or parameter named '{0}' was found in the CloudFormation template. Add the listener resource to the template or correct the reference name.",
258+
category: "AWSLambdaCSharpGenerator",
259+
DiagnosticSeverity.Error,
260+
isEnabledByDefault: true);
261+
262+
public static readonly DiagnosticDescriptor FromRouteNotSupportedOnAlb = new DiagnosticDescriptor(
263+
id: "AWSLambda0134",
264+
title: "FromRoute not supported on ALB functions",
265+
messageFormat: "[FromRoute] is not supported on ALB functions. ALB does not support route path template parameters. Use [FromHeader], [FromQuery], or [FromBody] instead.",
266+
category: "AWSLambdaCSharpGenerator",
267+
DiagnosticSeverity.Error,
268+
isEnabledByDefault: true);
269+
270+
public static readonly DiagnosticDescriptor AlbUnmappedParameter = new DiagnosticDescriptor(
271+
id: "AWSLambda0135",
272+
title: "Unmapped parameter on ALB function",
273+
messageFormat: "Parameter '{0}' on ALB function has no binding attribute. Use [FromHeader], [FromQuery], [FromBody], or [FromServices], or use the ApplicationLoadBalancerRequest or ILambdaContext types.",
274+
category: "AWSLambdaCSharpGenerator",
275+
DiagnosticSeverity.Error,
276+
isEnabledByDefault: true);
245277
}
246278
}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Extensions/ParameterListExtension.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,20 @@ public static bool HasConvertibleParameter(this IList<ParameterModel> parameters
1717
return false;
1818
}
1919

20+
// ALB request types are forwarded to lambda method if specified, there is no parameter conversion required.
21+
if (TypeFullNames.ALBRequests.Contains(p.Type.FullName))
22+
{
23+
return false;
24+
}
25+
2026
// ILambdaContext is forwarded to lambda method if specified, there is no parameter conversion required.
2127
if (p.Type.FullName == TypeFullNames.ILambdaContext)
2228
{
2329
return false;
2430
}
2531

2632
// Body parameter with target type as string doesn't require conversion because body is string by nature.
27-
if (p.Attributes.Any(att => att.Type.FullName == TypeFullNames.FromBodyAttribute) && p.Type.IsString())
33+
if (p.Attributes.Any(att => att.Type.FullName == TypeFullNames.FromBodyAttribute || att.Type.FullName == TypeFullNames.ALBFromBodyAttribute) && p.Type.IsString())
2834
{
2935
return false;
3036
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using Amazon.Lambda.Annotations.ALB;
5+
using Microsoft.CodeAnalysis;
6+
using System;
7+
using System.Linq;
8+
9+
namespace Amazon.Lambda.Annotations.SourceGenerator.Models.Attributes
10+
{
11+
/// <summary>
12+
/// Builder for <see cref="ALBApiAttribute"/>.
13+
/// </summary>
14+
public class ALBApiAttributeBuilder
15+
{
16+
public static ALBApiAttribute Build(AttributeData att)
17+
{
18+
if (att.ConstructorArguments.Length != 3)
19+
{
20+
throw new NotSupportedException($"{TypeFullNames.ALBApiAttribute} must have constructor with 3 arguments.");
21+
}
22+
23+
var listenerArn = att.ConstructorArguments[0].Value as string;
24+
var pathPattern = att.ConstructorArguments[1].Value as string;
25+
var priority = (int)att.ConstructorArguments[2].Value;
26+
27+
var data = new ALBApiAttribute(listenerArn, pathPattern, priority);
28+
29+
foreach (var pair in att.NamedArguments)
30+
{
31+
if (pair.Key == nameof(data.MultiValueHeaders) && pair.Value.Value is bool multiValueHeaders)
32+
{
33+
data.MultiValueHeaders = multiValueHeaders;
34+
}
35+
else if (pair.Key == nameof(data.HostHeader) && pair.Value.Value is string hostHeader)
36+
{
37+
data.HostHeader = hostHeader;
38+
}
39+
else if (pair.Key == nameof(data.HttpMethod) && pair.Value.Value is string httpMethod)
40+
{
41+
data.HttpMethod = httpMethod;
42+
}
43+
else if (pair.Key == nameof(data.ResourceName) && pair.Value.Value is string resourceName)
44+
{
45+
data.ResourceName = resourceName;
46+
}
47+
else if (pair.Key == nameof(data.HttpHeaderConditionName) && pair.Value.Value is string httpHeaderConditionName)
48+
{
49+
data.HttpHeaderConditionName = httpHeaderConditionName;
50+
}
51+
else if (pair.Key == nameof(data.HttpHeaderConditionValues) && !pair.Value.IsNull)
52+
{
53+
data.HttpHeaderConditionValues = pair.Value.Values.Select(v => v.Value as string).ToArray();
54+
}
55+
else if (pair.Key == nameof(data.QueryStringConditions) && !pair.Value.IsNull)
56+
{
57+
data.QueryStringConditions = pair.Value.Values.Select(v => v.Value as string).ToArray();
58+
}
59+
else if (pair.Key == nameof(data.SourceIpConditions) && !pair.Value.IsNull)
60+
{
61+
data.SourceIpConditions = pair.Value.Values.Select(v => v.Value as string).ToArray();
62+
}
63+
}
64+
65+
return data;
66+
}
67+
}
68+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using Amazon.Lambda.Annotations.ALB;
5+
using Microsoft.CodeAnalysis;
6+
7+
namespace Amazon.Lambda.Annotations.SourceGenerator.Models.Attributes
8+
{
9+
/// <summary>
10+
/// Builder for <see cref="ALB.FromHeaderAttribute"/>.
11+
/// </summary>
12+
public class ALBFromHeaderAttributeBuilder
13+
{
14+
public static ALB.FromHeaderAttribute Build(AttributeData att)
15+
{
16+
var data = new ALB.FromHeaderAttribute();
17+
foreach (var pair in att.NamedArguments)
18+
{
19+
if (pair.Key == nameof(data.Name) && pair.Value.Value is string value)
20+
{
21+
data.Name = value;
22+
}
23+
}
24+
25+
return data;
26+
}
27+
}
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using Amazon.Lambda.Annotations.ALB;
5+
using Microsoft.CodeAnalysis;
6+
7+
namespace Amazon.Lambda.Annotations.SourceGenerator.Models.Attributes
8+
{
9+
/// <summary>
10+
/// Builder for <see cref="ALB.FromQueryAttribute"/>.
11+
/// </summary>
12+
public class ALBFromQueryAttributeBuilder
13+
{
14+
public static ALB.FromQueryAttribute Build(AttributeData att)
15+
{
16+
var data = new ALB.FromQueryAttribute();
17+
foreach (var pair in att.NamedArguments)
18+
{
19+
if (pair.Key == nameof(data.Name) && pair.Value.Value is string value)
20+
{
21+
data.Name = value;
22+
}
23+
}
24+
25+
return data;
26+
}
27+
}
28+
}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/AttributeModelBuilder.cs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using Amazon.Lambda.Annotations.ALB;
23
using Amazon.Lambda.Annotations.APIGateway;
34
using Amazon.Lambda.Annotations.SQS;
45
using Microsoft.CodeAnalysis;
@@ -30,7 +31,7 @@ public static AttributeModel Build(AttributeData att, GeneratorExecutionContext
3031
else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.FromQueryAttribute), SymbolEqualityComparer.Default))
3132
{
3233
var data = FromQueryAttributeBuilder.Build(att);
33-
model = new AttributeModel<FromQueryAttribute>
34+
model = new AttributeModel<APIGateway.FromQueryAttribute>
3435
{
3536
Data = data,
3637
Type = TypeModelBuilder.Build(att.AttributeClass, context)
@@ -39,7 +40,7 @@ public static AttributeModel Build(AttributeData att, GeneratorExecutionContext
3940
else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.FromHeaderAttribute), SymbolEqualityComparer.Default))
4041
{
4142
var data = FromHeaderAttributeBuilder.Build(att);
42-
model = new AttributeModel<FromHeaderAttribute>
43+
model = new AttributeModel<APIGateway.FromHeaderAttribute>
4344
{
4445
Data = data,
4546
Type = TypeModelBuilder.Build(att.AttributeClass, context)
@@ -108,6 +109,42 @@ public static AttributeModel Build(AttributeData att, GeneratorExecutionContext
108109
Type = TypeModelBuilder.Build(att.AttributeClass, context)
109110
};
110111
}
112+
else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.ALBApiAttribute), SymbolEqualityComparer.Default))
113+
{
114+
var data = ALBApiAttributeBuilder.Build(att);
115+
model = new AttributeModel<ALBApiAttribute>
116+
{
117+
Data = data,
118+
Type = TypeModelBuilder.Build(att.AttributeClass, context)
119+
};
120+
}
121+
else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.ALBFromQueryAttribute), SymbolEqualityComparer.Default))
122+
{
123+
var data = ALBFromQueryAttributeBuilder.Build(att);
124+
model = new AttributeModel<ALB.FromQueryAttribute>
125+
{
126+
Data = data,
127+
Type = TypeModelBuilder.Build(att.AttributeClass, context)
128+
};
129+
}
130+
else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.ALBFromHeaderAttribute), SymbolEqualityComparer.Default))
131+
{
132+
var data = ALBFromHeaderAttributeBuilder.Build(att);
133+
model = new AttributeModel<ALB.FromHeaderAttribute>
134+
{
135+
Data = data,
136+
Type = TypeModelBuilder.Build(att.AttributeClass, context)
137+
};
138+
}
139+
else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.ALBFromBodyAttribute), SymbolEqualityComparer.Default))
140+
{
141+
var data = new ALB.FromBodyAttribute();
142+
model = new AttributeModel<ALB.FromBodyAttribute>
143+
{
144+
Data = data,
145+
Type = TypeModelBuilder.Build(att.AttributeClass, context)
146+
};
147+
}
111148
else
112149
{
113150
model = new AttributeModel

0 commit comments

Comments
 (0)