AWSCDK基础结构作为抽象数据类型,第三部分

2024-09-18

在我们CDK系列的第三部分,项目3,在相同的信息库,将用来说明一些先进的夸AWS集成特性,连同几个技巧特定的休息,众所周知,红帽实现雅加达休息规范。

让我们首先查看项目的pom.xml文件,它驱动了Maven构建进程。您将看到以下依赖项:

...

io.quarkiverse.amazonservices

quarkus-amazon-s3

io.quarkus

quarkus-amazon-lambda-http

io.quarkus

quarkus-rest-jackson

io.quarkus

quarkus-rest-client

...

software.amazon.awssdk

netty-nio-client

software.amazon.awssdk

url-connection-client

...

上面列表中的第一个依赖项,夸克斯-亚马逊-s3是一个Quarkus扩展,允许您的代码作为AWS S3客户端,并在桶中存储和删除对象,或实现备份和恢复策略,归档数据等。

下一个依赖项,夸克斯-亚马逊-lambda-http,是另一个旨在支持AWS HTTP网关API的夸克斯扩展。正如读者已经从本系列的前两个部分中所知道的那样,使用Quarkus,您可以使用AWS HTTP网关API或AWS REST网关API将REST API部署为AWS Lambda。这里我们将使用前一个,不那么扩展,因此提到的扩展。如果我们想使用AWS REST网关API,那么我们将不得不将夸克-亚马逊-http扩展替换为夸克-亚马逊-http扩展。

期待什么

在这个项目中,我们将使用Quarkus 3.11,在撰写本文时,它是最近的版本。与以前的版本相比,一些RESTeasy依赖已经发生了变化,因此依赖夸克-依赖-杰克逊取代了现在在3.10和以前使用的夸克-依赖依赖。此外,实现Eclipse MP REST客户端规范的夸克斯-休息-客户端扩展对于测试目的是必需的,我们将在稍后看到。最后但并非最不重要的是,需要url-连接-客户端Quarkus扩展,因为MP REST客户端实现默认使用它,因此,它必须包含在构建过程中。

现在,让我们来看看我们新的REST API。在cdk-quarkus-s3项目中打开Java类S3文件管理api,您将看到它定义了三个操作:下载文件、上传文件和列表文件。这三个应用程序都使用作为CDK应用程序堆栈的一部分创建的相同的S3桶。

Java

@Path("/s3")

public class S3FileManagementApi

{

@Inject

S3Client s3;

@ConfigProperty(name = "bucket.name")

String bucketName;

@POST

@Path("upload")

@Consumes(MediaType.MULTIPART_FORM_DATA)

public Response uploadFile(@Valid FileMetadata fileMetadata) throws Exception

{

PutObjectRequest request = PutObjectRequest.builder()

.bucket(bucketName)

.key(fileMetadata.filename)

.contentType(fileMetadata.mimetype)

.build();

s3.putObject(request, RequestBody.fromFile(fileMetadata.file));

return Response.ok().status(Response.Status.CREATED).build();

}

...

}

解释代码

上面的代码片段只复制了上传文件的操作,另外两个非常相似。通过利用Quarkus CDI,观察S3客户端的实例化是多么简单,它避免了需要多个样板代码行。此外,我们正在使用Eclipse MP配置规范来定义目标S3桶的名称。

我们的端点上传文件()接受POST请求并使用MULTIPART_FORM_DATA MIME数据分为两个不同的部分,一个用于有效负载,另一个包含要上传的文件。端点接受类文件元数据的输入参数,如下图所示:

Java

public class FileMetadata

{

@RestForm

@NotNull

public File file;

@RestForm

@PartType(MediaType.TEXT_PLAIN)

@NotEmpty

@Size(min = 3, max = 40)

public String filename;

@RestForm

@PartType(MediaType.TEXT_PLAIN)

@NotEmpty

@Size(min = 10, max = 127)

public String mimetype;

...

}

这个类是一个数据对象,将要上传的文件及其名称和MIME类型一起分组。它使用@RestForm RESTeasy特定注释来处理具有多部分/表单数据的HTTP请求。jakarta.validation.constraints注释的使用对于验证目的也非常实用。

要回到上面的端点,它创建一个putobbect请求,输入参数是目标桶名,一个唯一标识桶中存储文件的键,在这种情况下,是文件名和关联的MIME类型,例如文本文件的TEXT_PLAIN。一旦创建了输入请求,它将通过HTTP PUT请求发送到AWS S3服务。请注意,使用RequestBody.fromFile(…)语句将要上传的文件插入到请求体中是多么容易。

这都是作为AWSAambda函数公开的REST API的全部。现在让我们来看看在我们的CDK应用程序的堆栈中有什么新功能:

Java

...

HttpApi httpApi = HttpApi.Builder.create(this, "HttpApiGatewayIntegration")

.defaultIntegration(HttpLambdaIntegration.Builder.create("HttpApiGatewayIntegration", function).build()).build();

httpApiGatewayUrl = httpApi.getUrl();

CfnOutput.Builder.create(this, "HttpApiGatewayUrlOutput").value(httpApi.getUrl()).build();

...

这些行已经被添加到cdk-简单构造项目中的小桶构造类中。我们希望我们在当前堆栈中创建的Lambda函数位于HTTP网关的后面,并对其进行备份。这可能有一些好处。所以我们需要为我们的Lambda函数创建一个集成。

由AWS定义的集成的概念意味着为API端点提供一个后端。在HTTP网关的情况下,应该为每个API网关的端点提供一个或多个后端。这些集成有它们自己的请求和响应,不同于API本身。有两种集成类型:

· 其中后端是一个Lambda函数;

· HTTP集成,其中后端可能是任何已部署的web应用程序;

在我们的示例中,我们使用的是Lambda集成。还有两种类型的Lambda集成:

· Lambda代理集成,其中不需要集成的请求和响应的定义,以及它们到原始请求和响应的映射,因为它们是自动提供的;

· Lambda非代理集成,其中我们需要明确地指定如何将传入的请求数据映射到集成请求,以及如何将结果的集成响应数据映射到方法响应;

为了简单起见,我们在项目中使用了第一个案例。这就是上面的语句。默认集成(...)正在做的事情。一旦创建了集成,我们就需要显示新创建的API网关的URL,我们的Lambda函数是备份的。通过这种方式,除了能够像我们之前那样直接调用Lambda函数之外,我们还可以通过API网关来实现它。在一个有几十个REST端点的项目中,有一个单一的接触点非常重要,在那里应用安全策略、日志记录、日志化和其他交叉问题。API网关是一个理想的单个接触点。

该项目附带了两个单元测试和集成测试。例如,类S3文件管理测试使用REST保证执行单元测试,如下图所示:

Java

@QuarkusTest

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)

public class S3FileManagementTest

{

private static File readme = new File("./src/test/resources/README.md");

@Test

@Order(10)

public void testUploadFile()

{

given()

.contentType(MediaType.MULTIPART_FORM_DATA)

.multiPart("file", readme)

.multiPart("filename", "README.md")

.multiPart("mimetype", MediaType.TEXT_PLAIN)

.when()

.post("/s3/upload")

.then()

.statusCode(HttpStatus.SC_CREATED);

}

@Test

@Order(20)

public void testListFiles()

{

given()

.when().get("/s3/list")

.then()

.statusCode(200)

.body("size()", equalTo(1))

.body("[0].objectKey", equalTo("README.md"))

.body("[0].size", greaterThan(0));

}

@Test

@Order(30)

public void testDownloadFile() throws IOException

{

given()

.pathParam("objectKey", "README.md")

.when().get("/s3/download/{objectKey}")

.then()

.statusCode(200)

.body(equalTo(Files.readString(readme.toPath())));

}

}

这个单元测试首先将文件README.md上传到为此目的定义的S3桶。然后它列出桶中存在的所有文件,并通过下载刚刚上传的文件完成。请注意应用程序。属性文件中的以下几行:

Plain Text

bucket.name=my-bucket-8701

%test.quarkus.s3.devservices.buckets=${bucket.name}

第一个定义目标桶的名称,第二个定义自动创建目标桶。这只在通过Quarkus Mock服务器执行时工作。当这个单元测试是在Maven测试阶段执行的,但是针对由Quarkus自动管理的测试容器运行的本地堆栈实例,一旦部署了CDK应用程序,集成实例,S3文件管理,是针对真正的AWS基础设施执行的。

集成测试使用了一种不同的范式,它们利用了Eclipse客户端规范,取代了由Quarkus实现的EPREST客户端规范,如下代码片段所示:

Java

@QuarkusTest

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)

public class S3FileManagementIT

{

private static File readme = new File("./src/test/resources/README.md");

@Inject

@RestClient

S3FileManagementClient s3FileManagementTestClient;

@Inject

@ConfigProperty(name = "base_uri/mp-rest/url")

String baseURI;

@Test

@Order(40)

public void testUploadFile() throws Exception

{

Response response = s3FileManagementTestClient.uploadFile(new FileMetadata(readme, "README.md", MediaType.TEXT_PLAIN));

assertThat(response).isNotNull();

assertThat(response.getStatusInfo().toEnum()).isEqualTo(Response.Status.CREATED);

}

...

}

我们注入了S3文件管理客户端,这是一个定义API端点的简单接口,Quarkus也完成了剩下的工作。它将生成所需的客户端代码。我们只需要在这个接口上调用端点,例如上传文件(……),仅此而已。看看cdk-夸库斯-s3项目中的S3文件管理客户端,看看一切是如何工作的,请注意注释是如何定义一个名为base_uri的配置键,在部署.sh脚本中进一步使用的。

现在,要测试AWS的真实基础设施,您需要执行deplend.sh脚本,如下所示:

Shell

$ cd cdk

$ ./deploy.sh cdk-quarkus/cdk-quarkus-api-gateway cdk-quarkus/cdk-quarkus-s3

这将编译和构建应用程序,执行单元测试,在AWS上部署Cloud形成堆栈,并针对此基础设施执行集成测试。在执行结束时,你应该看到类似的东西:

Plain Text

Outputs:

QuarkusApiGatewayStack.FunctionURLOutput = https://.lambda-url.eu-west-3.on.aws/

QuarkusApiGatewayStack.LambdaWithBucketConstructIdHttpApiGatewayUrlOutput = https://.execute-api.eu-west-3.amazonaws.com/

Stack ARN:

arn:aws:cloudformation:eu-west-3:...:stack/QuarkusApiGatewayStack/

现在,除了您在前面的示例中已经看到的Lambda函数URL之外,您还可以看到API HTTP网关URL现在可以用于测试目的,而不是Lambda。

同时,还提供了一个从邮递员(S3文件管理网)导出的E2E测试用例。它是通过Docker图像邮递员/新手来执行的:最新的,在测试容器中运行。下面是一个片段:Java

@QuarkusTest

public class S3FileManagementPostmanIT

{

...

private static GenericContainer postman = new GenericContainer<>("postman/newman")

.withNetwork(Network.newNetwork())

.withCopyFileToContainer(MountableFile.forClasspathResource("postman/AWS.postman_collection.json"),

"/etc/newman/AWS.postman_collection.json")

.withStartupCheckStrategy(new OneShotStartupCheckStrategy().withTimeout(Duration.ofSeconds(10)));

@Test

public void run()

{

String apiEndpoint = System.getenv("API_ENDPOINT");

assertThat(apiEndpoint).isNotEmpty();

postman.withCommand("run", "AWS.postman_collection.json",

"--global-var base_uri=" + apiEndpoint.substring(8).replaceAll(".$", ""));

postman.start();

LOG.info(postman.getLogs());

assertThat(postman.getCurrentContainerInfo().getState().getExitCodeLong()).isZero();

postman.stop();

}

}

结论

正如你所看到的,启动邮递员/新闻:最新图像测试容器,我们运行E2E测试用例输出从邮递员传递选项全局变量标记base_uri的初始化保持API网址的值保存的部署.sh脚本在API端点环境变量。不幸的是,可能是由于一个错误,邮差/新闻人员的图像不能识别这个选项,因此,等待这个问题被修复,这个测试现在被禁用了。

当然,您可以在邮差中导入文件AWS.postman_collection.json,并在用AWS生成的API URL的当前值替换全局变量{{base_uri}}后以这种方式运行它。

相关推荐