fix(cloudfront): reject a function runtime that CloudFront does not support (#5445)
Closes #5425 Adapted: kept only the runtime check; the duplicate-name check that surrounds it upstream comes from a later CloudFront Functions change not taken here. (cherry picked from commit 8deac0a4244449470d6bd4bfa8769f8921d1533d)
This commit is contained in:
@@ -59,6 +59,7 @@ public class CloudFrontService {
|
|||||||
Set.of("s3", "mediastore", "mediapackagev2", "lambda");
|
Set.of("s3", "mediastore", "mediapackagev2", "lambda");
|
||||||
private static final Set<String> OAC_SIGNING_BEHAVIORS =
|
private static final Set<String> OAC_SIGNING_BEHAVIORS =
|
||||||
Set.of("always", "never", "no-override");
|
Set.of("always", "never", "no-override");
|
||||||
|
private static final Set<String> FUNCTION_RUNTIMES = Set.of("cloudfront-js-1.0", "cloudfront-js-2.0");
|
||||||
private static final int MAX_ORIGIN_CUSTOM_HEADERS = 30;
|
private static final int MAX_ORIGIN_CUSTOM_HEADERS = 30;
|
||||||
private static final int MAX_CUSTOM_HEADER_NAME_LENGTH = 256;
|
private static final int MAX_CUSTOM_HEADER_NAME_LENGTH = 256;
|
||||||
private static final int MAX_CUSTOM_HEADER_VALUE_LENGTH = 1_783;
|
private static final int MAX_CUSTOM_HEADER_VALUE_LENGTH = 1_783;
|
||||||
@@ -1227,6 +1228,7 @@ public class CloudFrontService {
|
|||||||
// ── CloudFront Functions ──────────────────────────────────────────────────
|
// ── CloudFront Functions ──────────────────────────────────────────────────
|
||||||
|
|
||||||
public synchronized CloudFrontFunction createFunction(CloudFrontFunction fn) {
|
public synchronized CloudFrontFunction createFunction(CloudFrontFunction fn) {
|
||||||
|
validateFunctionRuntime(fn.getRuntime());
|
||||||
fn.setStage("DEVELOPMENT");
|
fn.setStage("DEVELOPMENT");
|
||||||
fn.setStatus("UNPUBLISHED");
|
fn.setStatus("UNPUBLISHED");
|
||||||
fn.setEtag(UUID.randomUUID().toString());
|
fn.setEtag(UUID.randomUUID().toString());
|
||||||
@@ -1251,6 +1253,7 @@ public class CloudFrontService {
|
|||||||
|
|
||||||
public synchronized CloudFrontFunction updateFunction(String name, String ifMatch,
|
public synchronized CloudFrontFunction updateFunction(String name, String ifMatch,
|
||||||
CloudFrontFunction updated) {
|
CloudFrontFunction updated) {
|
||||||
|
validateFunctionRuntime(updated.getRuntime());
|
||||||
CloudFrontFunction existing = describeFunction(name, null);
|
CloudFrontFunction existing = describeFunction(name, null);
|
||||||
if (!existing.getEtag().equals(ifMatch)) {
|
if (!existing.getEtag().equals(ifMatch)) {
|
||||||
throw new AwsException("InvalidIfMatchVersion",
|
throw new AwsException("InvalidIfMatchVersion",
|
||||||
@@ -1323,6 +1326,13 @@ public class CloudFrontService {
|
|||||||
return effectiveStage;
|
return effectiveStage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void validateFunctionRuntime(String runtime) {
|
||||||
|
if (runtime != null && !FUNCTION_RUNTIMES.contains(runtime)) {
|
||||||
|
throw new AwsException("InvalidArgument",
|
||||||
|
"The parameter Runtime must be cloudfront-js-1.0 or cloudfront-js-2.0.", 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static AwsException invalidFunctionStage(String stage) {
|
private static AwsException invalidFunctionStage(String stage) {
|
||||||
return new AwsException("InvalidArgument",
|
return new AwsException("InvalidArgument",
|
||||||
"The parameter Stage must be DEVELOPMENT or LIVE: " + stage, 400);
|
"The parameter Stage must be DEVELOPMENT or LIVE: " + stage, 400);
|
||||||
|
|||||||
+32
@@ -109,6 +109,38 @@ class CloudFrontManagementProtocolTest {
|
|||||||
equalTo("InvalidArgument")));
|
equalTo("InvalidArgument")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createFunctionRejectsAnUnknownRuntime() {
|
||||||
|
String name = "runtime-test-" + UUID.randomUUID();
|
||||||
|
String createBody = """
|
||||||
|
<CreateFunctionRequest xmlns="http://cloudfront.amazonaws.com/doc/2020-05-31/">
|
||||||
|
<Name>%s</Name>
|
||||||
|
<FunctionConfig>
|
||||||
|
<Comment>routing function</Comment>
|
||||||
|
<Runtime>python3</Runtime>
|
||||||
|
</FunctionConfig>
|
||||||
|
<FunctionCode>function handler(event) { return event.request; }</FunctionCode>
|
||||||
|
</CreateFunctionRequest>
|
||||||
|
""".formatted(name);
|
||||||
|
|
||||||
|
given()
|
||||||
|
.contentType("application/xml")
|
||||||
|
.body(createBody)
|
||||||
|
.when()
|
||||||
|
.post(API + "function")
|
||||||
|
.then()
|
||||||
|
.statusCode(400)
|
||||||
|
.body(hasXPath(
|
||||||
|
"//*[local-name()='Code']/text()",
|
||||||
|
equalTo("InvalidArgument")));
|
||||||
|
|
||||||
|
given()
|
||||||
|
.when()
|
||||||
|
.get(API + "function/" + name + "/describe")
|
||||||
|
.then()
|
||||||
|
.statusCode(404);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void describeAndPublishFunctionPreserveBothStages() {
|
void describeAndPublishFunctionPreserveBothStages() {
|
||||||
String name = "describe-test-" + UUID.randomUUID();
|
String name = "describe-test-" + UUID.randomUUID();
|
||||||
|
|||||||
+48
@@ -164,6 +164,54 @@ class CloudFrontServiceTest {
|
|||||||
assertNotEquals(development.getEtag(), live.getEtag());
|
assertNotEquals(development.getEtag(), live.getEtag());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createFunction_unknownRuntime_isRejected() {
|
||||||
|
CloudFrontService service = serviceWithDomainSuffix("cloudfront.net");
|
||||||
|
CloudFrontFunction function = new CloudFrontFunction();
|
||||||
|
function.setName("routing");
|
||||||
|
function.setRuntime("python3");
|
||||||
|
|
||||||
|
AwsException error = assertThrows(AwsException.class, () -> service.createFunction(function));
|
||||||
|
|
||||||
|
assertEquals("InvalidArgument", error.getErrorCode());
|
||||||
|
assertEquals(400, error.getHttpStatus());
|
||||||
|
assertThrows(AwsException.class, () -> service.describeFunction("routing", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createFunction_supportedRuntimes_areAccepted() {
|
||||||
|
CloudFrontService service = serviceWithDomainSuffix("cloudfront.net");
|
||||||
|
for (String runtime : List.of("cloudfront-js-1.0", "cloudfront-js-2.0")) {
|
||||||
|
CloudFrontFunction function = new CloudFrontFunction();
|
||||||
|
function.setName("routing-" + runtime);
|
||||||
|
function.setRuntime(runtime);
|
||||||
|
|
||||||
|
assertEquals(runtime, service.createFunction(function).getRuntime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void updateFunction_unknownRuntime_isRejectedAndKeepsTheFunction() {
|
||||||
|
CloudFrontService service = serviceWithDomainSuffix("cloudfront.net");
|
||||||
|
CloudFrontFunction function = new CloudFrontFunction();
|
||||||
|
function.setName("routing");
|
||||||
|
function.setFunctionCode("first");
|
||||||
|
function.setRuntime("cloudfront-js-2.0");
|
||||||
|
String etag = service.createFunction(function).getEtag();
|
||||||
|
CloudFrontFunction update = new CloudFrontFunction();
|
||||||
|
update.setFunctionCode("second");
|
||||||
|
update.setRuntime("python3");
|
||||||
|
|
||||||
|
AwsException error = assertThrows(AwsException.class,
|
||||||
|
() -> service.updateFunction("routing", etag, update));
|
||||||
|
|
||||||
|
assertEquals("InvalidArgument", error.getErrorCode());
|
||||||
|
CloudFrontFunction kept = service.describeFunction("routing", null);
|
||||||
|
assertEquals("first", kept.getFunctionCode());
|
||||||
|
assertEquals("cloudfront-js-2.0", kept.getRuntime());
|
||||||
|
assertEquals(etag, kept.getEtag());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void listFunctionsWithoutStageReturnsDevelopmentAndLive() {
|
void listFunctionsWithoutStageReturnsDevelopmentAndLive() {
|
||||||
CloudFrontService service = serviceWithDomainSuffix("cloudfront.net");
|
CloudFrontService service = serviceWithDomainSuffix("cloudfront.net");
|
||||||
|
|||||||
Reference in New Issue
Block a user