测试S3A文件系统客户端及其功能

本模块包含单元测试和集成测试:单元测试可独立运行,无需连接S3服务;集成测试则需要有效连接S3以便与存储桶交互。单元测试套件遵循Test*.java命名规范,集成测试则遵循ITest*.java命名规范。

影响hadoop-aws模块的补丁提交策略

由于需要保护凭证安全,Apache Jenkins基础设施不运行任何S3集成测试。

任何补丁的提交者都需要运行所有集成测试,并声明他们使用的S3区域/实现。

重要提示:未包含此声明的补丁将被忽略

该策略已被证明是保证代码变更完全回归测试的唯一机制。为什么需要声明区域?有两个原因

  1. 它帮助我们识别仅针对特定端点或S3协议的第三方实现才会出现的回归问题。
  2. 它迫使提交者在测试方面更加诚实。很容易撒谎说"是的,我测试过这个",但要说"是的,我在S3 US-west上测试过这个"就是更具体的谎言,更难编造。而且,如果被发现:你在项目中将失去所有信誉。

你不需要在AWS基础设施内的虚拟机中进行测试;使用-Dparallel-tests选项时,非规模测试可在二十分钟内完成。由于测试完成后会自动清理,这些测试也被设计为低成本。运行测试既不困难也不昂贵;如果你无法运行测试,就无法保证你的补丁有效。评审人员已经有足够多的工作要做,没有时间进行这些测试,尤其是每次失败都会导致开发迭代变慢的情况下。

Please: run the tests. And if you don’t, we are sorry for declining your patch, but we have to.

如果测试出现间歇性故障怎么办?

某些测试会间歇性失败,特别是在并行运行时。如果发生这种情况,可以尝试单独运行该测试以查看是否成功。

如果仍然失败,请在您的声明中包含这一事实。我们知道某些测试偶尔不可靠。

如果测试因网络连接超时或失败怎么办?

测试和S3A客户端设计为可配置不同的超时设置。如果您发现问题且此配置无效,这表明配置机制尚不完善。如果在生产代码中出现这种情况,可能预示着在长距离连接中会暴露问题。请协助我们识别并修复这些问题——尤其是您最适合验证修复是否有效。

设置测试

为了对S3*文件系统客户端进行集成测试,你需要提供auth-keys.xml文件,该文件会将认证详细信息传递给测试运行器。

这是一个Hadoop XML配置文件,必须放置在hadoop-tools/hadoop-aws/src/test/resources目录下。

文件 core-site.xml

该文件预先存在,并引用了在auth-keys.xml下创建的配置。

在大多数情况下,除非您需要在测试期间应用特定的非默认属性更改,否则您无需编辑此文件。

文件 auth-keys.xml

该文件的存在会触发对S3类的测试。

如果没有这个文件,该模块中的所有集成测试都不会被执行

XML文件必须包含连接每个文件系统客户端到对象存储所需的所有ID/密钥信息,以及每个文件系统用于测试的URL。

  1. test.fs.s3a.name : S3a测试所用的存储桶URL
  2. fs.contract.test.fs.s3a : 用于S3a文件系统合约测试的存储桶URL

测试过程中,存储桶的内容将被销毁:请勿将此存储桶用于测试以外的任何用途。此外,对于s3a,测试开始时将中止所有进行中的多部分上传(通过强制设置fs.s3a.multipart.purge=true)以清理之前失败测试的临时状态。

示例:

<configuration>

  <property>
    <name>test.fs.s3a.name</name>
    <value>s3a://test-aws-s3a/</value>
  </property>

  <property>
    <name>fs.contract.test.fs.s3a</name>
    <value>${test.fs.s3a.name}</value>
  </property>

  <property>
    <name>fs.s3a.access.key</name>
    <description>AWS access key ID. Omit for IAM role-based authentication.</description>
    <value>DONOTCOMMITTHISKEYTOSCM</value>
  </property>

  <property>
    <name>fs.s3a.secret.key</name>
    <description>AWS secret key. Omit for IAM role-based authentication.</description>
    <value>DONOTEVERSHARETHISSECRETKEY!</value>
  </property>

  <property>
    <name>test.sts.endpoint</name>
    <description>Specific endpoint to use for STS requests.</description>
    <value>sts.amazonaws.com</value>
  </property>

</configuration>

配置S3a加密

为了使S3a加密测试正确运行,必须在s3a合约xml文件或auth-keys.xml文件中配置fs.s3a.encryption.key,并使用AWS KMS加密密钥ARN作为其值,因为该值对每个AWS KMS都不同。请注意,此KMS密钥应在与您的S3存储桶相同的区域中创建。否则,您可能会遇到KMS.NotFoundException错误。

示例:

<property>
  <name>fs.s3a.encryption.key</name>
  <value>arn:aws:kms:us-west-2:360379543683:key/071a86ff-8881-4ba0-9230-95af6d01ca01</value>
</property>

您还可以通过在s3a合约文件中配置属性fs.s3a.encryption.algorithm来强制所有测试使用特定的SSE加密方法运行。

默认加密

可以在AWS端为存储桶配置默认加密。当启用默认加密时,由于ETags生成方式的不确定性,会跳过部分S3AFileSystem测试。

禁用加密测试

如果S3存储/存储类别不支持服务器端加密,这些操作将会失败。可以关闭这些功能。

<property>
  <name>test.fs.s3a.encryption.enabled</name>
  <value>false</value>
</property>

加密仅用于类名中包含Encryption的特定测试套件。

运行测试

完成配置后,通过Maven执行测试运行。

mvn clean verify

还可以通过在命令行传递parallel-tests属性来并行执行多个测试套件。这些测试大部分时间都阻塞在与S3服务的网络I/O上,因此并行运行往往能更快完成完整测试流程。

mvn -Dparallel-tests clean verify

某些测试必须独占访问S3存储桶才能运行,因此即使设置了parallel-tests属性,多个测试套件仍将在并行测试完成后,在单独的Maven执行步骤中串行运行。

默认情况下,parallel-tests会并行运行4个测试套件。可以通过传递testsThreadCount属性来调整这个数值。

mvn -Dparallel-tests -DtestsThreadCount=8 clean verify

如果只想运行单元测试(这些测试不需要S3连接或AWS凭证),可以使用上述任意调用方式,但需将目标从verify改为test

mvn clean test

mvn -Dparallel-tests clean test

mvn -Dparallel-tests -DtestsThreadCount=8 clean test

要仅运行特定命名的测试子集,对于单元测试请传递test属性,对于集成测试请传递it.test属性。

mvn clean test -Dtest=TestS3AInputPolicies

mvn clean verify -Dit.test=ITestS3AFileContextStatistics -Dtest=none

mvn clean verify -Dtest=TestS3A* -Dit.test=ITestS3A*

请注意,当运行特定的测试子集时,传入testit.test的模式会覆盖配置中关于哪些测试需要在单独的串行阶段隔离运行的要求(如上所述)。这可能导致不可预测的结果,因此建议避免同时传递parallel-teststestit.test参数。如果您确定指定的测试可以安全并行运行,那么这种方式是可行的。但对于宽泛的模式(如上面展示的ITestS3A*),可能会导致不可预测的测试失败。

针对不同区域的测试

S3A可以连接到不同区域——测试支持此功能。只需在auth-keys.xml中定义目标区域即可。

<property>
  <name>fs.s3a.endpoint.region</name>
  <value>eu-central-1</value>
</property>

CSV数据测试

TestS3AInputStreamPerformance测试需要读取一个多MB的文本文件。这些测试默认使用的公共文件来自NOAA Continuously Operating Reference Stations (CORS) Network (NCN),路径为s3a://noaa-cors-pds/raw/2023/001/akse/AKSE001a.23_.gz

过去要求必须使用csv.gz文件来验证S3 Select支持。现在由于S3 Select支持已被移除,可以使用其他大型文件替代。

该对象的路径在选项 fs.s3a.scale.test.csvfile 中设置,

<property>
  <name>fs.s3a.scale.test.csvfile</name>
  <value>s3a://noaa-cors-pds/raw/2023/001/akse/AKSE001a.23_.gz</value>
</property>
  1. 如果未覆盖该选项,则使用默认值。该服务托管在亚马逊的美国东部数据中心。
  2. 如果fs.s3a.scale.test.csvfile为空,则依赖该文件的测试将被跳过。
  3. 如果由于任何原因无法读取数据,则测试将失败。
  4. 如果该属性设置为不同的路径,则该数据必须可读且“足够”大。
  5. 如果是.gz文件,预计会出现与解压缩相关的测试失败。

(需要空格或换行的原因是添加“一个空条目”;空的会被视为未定义并采用默认值)

如果在不同的AWS S3区域使用测试文件,则必须定义特定于存储桶的区域。对于托管在noaa-cors-pds存储桶中的默认测试数据集,配置如下:

  <property>
    <name>fs.s3a.bucket.noaa-cors-pds.endpoint.region</name>
    <value>us-east-1</value>
  </property>

测试访问点集成

S3a支持使用访问点ARN来访问S3中的数据。如果您认为您的更改会影响VPC集成、请求签名、ARN操作或任何涉及实际向S3发送和从S3检索数据的代码路径,请确保在启用此功能的情况下运行整个集成测试套件。

查看我们的文档了解如何启用此功能的步骤。要为您的S3存储桶创建访问点,您可以使用AWS控制台或CLI。

查看集成测试报告

Integration test results and logs are stored in target/failsafe-reports/. An HTML report can be generated during site generation, or with the surefire-report plugin:

mvn surefire-report:failsafe-report-only

测试版本化存储

某些测试(特别是ITestS3ARemoteFileChanged中的部分测试)需要版本化存储桶才能实现完整的测试覆盖。

要在存储桶中启用版本控制。

  1. 在AWS S3管理控制台中查找并选择存储桶。
  2. 在“属性”选项卡中,将其设置为版本控制。
  3. 重要 创建生命周期规则,在24小时后自动清理旧版本。这可以避免测试运行创建后又删除的对象产生额外费用。
  4. 再次运行测试。

一旦存储桶转换为版本控制状态,就无法再转换回非版本控制状态。

测试不同的标记保留策略

Hadoop支持不同的目录标记保留策略 - 本质上是经典的“删除”选项和更高性能的“保留”选项;"权威"模式只是将"保留"限制在存储桶的某个部分。

示例:使用markers=keep进行测试

mvn verify -Dparallel-tests -DtestsThreadCount=4 -Dmarkers=keep

这是默认设置,无需显式指定。

示例:使用markers=delete进行测试

mvn verify -Dparallel-tests -DtestsThreadCount=4 -Dmarkers=delete

示例:使用markers=authoritative进行测试

mvn verify -Dparallel-tests -DtestsThreadCount=4 -Dmarkers=authoritative

除非存储桶中的路径实际配置为混合状态,否则此最终选项用途有限;如果未进行任何设置,结果应与“删除”选项相同

启用标记审计功能

要启用对每个测试套件输出目录的审计功能,请启用选项 fs.s3a.directory.marker.audit

-Dfs.s3a.directory.marker.audit=true

设置后,如果标记策略是删除测试输出目录下的标记,则将运行标记工具审计命令。如果发现标记,此操作将失败。

这会给每个操作增加额外开销,但有助于验证连接器不会在需要删除标记的位置保留标记——从而保持向后兼容性。

为所有测试启用预取

如果Maven构建中设置了prefetch属性,测试将在预取模式下运行。这也可以与规模测试结合使用。

mvn verify -Dprefetch

mvn verify -Dparallel-tests -Dprefetch -DtestsThreadCount=8

mvn verify -Dparallel-tests -Dprefetch -Dscale -DtestsThreadCount=8

规模测试

有一组专门用于衡量S3A测试可扩展性和大规模性能的测试,称为规模测试。测试内容包括:创建和遍历目录树、上传大文件、重命名文件、删除文件、在文件中定位、执行随机IO操作等。这使得它们成为基准测试的基础组成部分。

从本质上讲,它们速度很慢。而且,由于它们的执行时间通常受限于运行测试的计算机与S3端点之间的带宽,并行执行并不能加速这些测试。

启用规模测试

如果Maven构建中设置了scale属性,则会启用测试,无论是否使用了并行测试配置文件都可以这样做

mvn verify -Dscale

mvn verify -Dparallel-tests -Dscale -DtestsThreadCount=8

最占用带宽的测试(那些上传数据的测试)总是按顺序运行;由于HTTPS设置成本或服务器端操作而较慢的测试则包含在并行测试集中。

通过Maven调整规模选项

部分测试可以通过Maven构建或运行测试时使用的配置文件进行调整。

mvn verify -Dparallel-tests -Dscale -DtestsThreadCount=8 -Dfs.s3a.scale.test.huge.filesize=128M

该算法是

  1. 该值从配置文件中查询,如果未设置则使用默认值。
  2. 该值是从JVM系统属性中查询获取的,由maven传递下来。
  3. 如果系统属性为null、空字符串或其值为unset,则使用配置值。unset选项用于解决maven属性传播中的一个特殊问题

只有少数属性可以通过这种方式设置;未来会添加更多。

属性 含义
fs.s3a.scale.test.timeout Timeout in seconds for scale tests
fs.s3a.scale.test.huge.filesize Size for huge file uploads
fs.s3a.scale.test.huge.huge.partitionsize Size for partitions in huge file uploads

文件和分区大小是带有k/m/g/t/p后缀的数值,具体取决于所需大小。例如:128M、128m、2G、2G、4T甚至1P。

扩展测试配置选项

一些规模测试会执行多项操作(例如创建多个目录)。

要执行的操作的确切数量可在选项scale.test.operation.count中配置

<property>
  <name>scale.test.operation.count</name>
  <value>10</value>
</property>

较大的值会产生更大的负载,建议在本地测试或批量运行时使用。

较小的值会加快测试运行速度,尤其是当对象存储距离较远时。

对目录进行操作的功能有一个单独的选项:用于控制创建递归目录时的测试宽度和深度。数值越大,创建的目录数量会呈指数级增长,从而影响性能。

<property>
  <name>scale.test.directory.count</name>
  <value>2</value>
</property>

针对S3A的DistCp测试支持可配置的文件大小。默认值为10 MB,但配置值以KB表示,以便可以调小以实现更快的测试运行。

<property>
  <name>scale.test.distcp.file.size.kb</name>
  <value>10240</value>
</property>

以下是S3A特定的扩展测试属性

fs.s3a.scale.test.huge.filesize: "超大文件测试"的大小(以MB为单位)。

Huge File测试验证了S3A处理大文件的能力——属性fs.s3a.scale.test.huge.filesize声明了要使用的文件大小。

<property>
  <name>fs.s3a.scale.test.huge.filesize</name>
  <value>200M</value>
</property>

Amazon S3对大文件(超过5GB)的处理方式与小文件不同。将超大文件大小设置为大于该值的数字可验证对大文件的支持。

<property>
  <name>fs.s3a.scale.test.huge.filesize</name>
  <value>6G</value>
</property>

这种规模的测试速度较慢:最好在运行于云基础设施中的主机上执行,其中S3端点位于该基础设施中。否则,请在fs.s3a.scale.test.timeout中设置较大的超时值。

<property>
  <name>fs.s3a.scale.test.timeout</name>
  <value>432000</value>
</property>

测试按特定顺序执行,以确保仅在全部测试结束后才清理生成的文件。如果测试被中断,测试数据将保留。

通过持续集成进行测试

并行CI构建。

对于该模块的CI测试,包括集成测试,通常需要支持同时测试多个PR。

要实现这一点:1. 必须在job.id属性中提供一个作业ID,这样每个作业都能在独立的目录树中运行。这应该是一个数字或唯一字符串,将用作路径元素,因此只能包含S3/hadoop路径元素中有效的字符。2. 需要通过将fs.s3a.root.tests.enabled设置为false来禁用根目录测试,可以在maven命令行或XML配置中进行设置。

mvn verify -T 1C -Dparallel-tests -DtestsThreadCount=14 -Dscale -Dfs.s3a.root.tests.enabled=false -Djob.id=001

此并行执行功能仅适用于共享单个S3存储桶的隔离构建;不支持来自同一本地源代码树的并行构建和测试。

在不执行根测试的情况下,设置一个定时任务定期清理测试存储桶中的所有数据,以降低成本。最简单的方法是为存储桶设置生命周期规则,删除超过几天的所有文件,同时终止所有超过24小时的待处理上传。

保护CI构建安全

在CI基础设施中使用AWS凭证测试提交到Apache GitHub账户的PR显然是不安全的——这就是为什么Yetus发起的构建不会这样做。

任何私下进行此操作的人员应:* 在触发测试前审查传入的补丁。* 拥有一个专用的IAM角色,该角色对测试存储桶、使用的任何KMS密钥以及包含CSV测试文件的外部存储桶具有受限访问权限。* 建立一个构建流程,为该角色生成短期会话凭据。* 在EC2虚拟机/容器中运行测试,该虚拟机/容器从IAM实例/容器凭据提供程序收集受限的IAM凭据。

负载测试。

某些测试旨在通过每秒发送超出AWS账户允许范围的请求来使AWS服务过载。

这些测试的操作可能对同一账户的其他用户可见——尤其是当他们正在测试目标所在的AWS区域工作时。

也可能产生更高的费用。

这些测试的前缀都是 ILoadTest

它们不会自动运行:必须从命令行或集成开发环境(IDE)中显式启动。

在执行前查看源代码并阅读Javadocs文档。

注意:这里的一个担忧是,短时间内请求过多的会话/角色凭证可能会导致账户被锁定在某个区域之外。实际上并不会:它只会触发STS请求的限流。

针对非AWS S3存储的测试。

S3A文件系统旨在与实现S3协议的存储服务协同工作,只要这些服务能够通过亚马逊S3 SDK进行通信。我们鼓励对其他文件系统进行测试,并提交修复问题的补丁。特别是,我们建议对Hadoop候选版本进行测试,因为这些第三方端点的测试覆盖率甚至低于S3端点本身。

用于关闭第三方存储上不可用功能测试的核心XML设置。

  <property>
    <name>test.fs.s3a.encryption.enabled</name>
    <value>false</value>
  </property>
  <property>
    <name>test.fs.s3a.create.storage.class.enabled</name>
    <value>false</value>
  </property>
  <property>
    <name>test.fs.s3a.sts.enabled</name>
    <value>false</value>
  </property>
  <property>
    <name>test.fs.s3a.create.create.acl.enabled</name>
    <value>false</value>
  </property>

有关此主题的更多信息,请参阅第三方商店

测试中使用的公共数据集

部分测试依赖于Amazon S3上现有的公开数据集。您可以在org.apache.hadoop.fs.s3a.test.PublicDatasetTestUtils中找到其中一些数据集。

当测试不属于Amazon S3标准商业分区(aws)的端点时,例如第三方实现或AWS中国区域,您应该将这些配置替换为空()以禁用测试,或替换为对象存储中支持这些测试的现有路径。

例如,MarkerTools测试可能需要一个包含大量对象的存储桶,或者请求者支付测试需要为存储桶启用请求者支付功能。

禁用存储类测试

当针对不支持S3存储类别的第三方对象存储运行存储类别测试时,这些测试可能会失败。可以禁用它们。

<property>
  <name>test.fs.s3a.create.storage.class.enabled</name>
  <value>false</value>
</property>

配置CSV文件读取测试

要在支持相同API的替代基础设施上进行测试,必须将选项fs.s3a.scale.test.csvfile设置为" ",或者将一个至少10MB的对象上传到对象存储中,并将fs.s3a.scale.test.csvfile选项设置为其路径。

<property>
  <name>fs.s3a.scale.test.csvfile</name>
  <value> </value>
</property>

(是的,这个空格是必要的。Hadoop Configuration 类会将空值视为"不覆盖默认值")。

为所有测试启用预取

如果Maven构建中设置了prefetch属性,测试将在预取模式下运行。这也可以与规模测试结合使用。

如果ITestS3AContractGetFileStatusV1List因任何关于不支持的API的错误而失败。

  <property>
    <name>test.fs.s3a.list.v1.enabled</name>
    <value>false</value>
  </property>

注意:没有对应的关闭v2列表API的选项,现在所有存储都必须支持该功能。

测试请求者付费

默认情况下,请求者支付测试将在亚马逊S3的us-east-1区域寻找存在的存储桶。

如果终端节点支持请求者付费,您可以指定一个替代对象。测试仅需要一个至少几字节大小的对象,以验证列表和基本读取功能是否正常工作。

<property>
  <name>test.fs.s3a.requester.pays.file</name>
  <value>s3a://my-req-pays-enabled-bucket/on-another-endpoint.json</value>
</property>

如果端点不支持请求者付费,您也可以通过将测试URI配置为单个空格来禁用测试。

<property>
  <name>test.fs.s3a.requester.pays.file</name>
  <value> </value>
</property>

测试会话凭证

部分测试会从AWS安全令牌服务请求会话凭证和假定角色凭证,然后直接或通过委托令牌使用这些凭证与S3进行身份验证。

如果S3实现不支持STS,则必须禁用这些功能测试用例:

<property>
  <name>test.fs.s3a.sts.enabled</name>
  <value>false</value>
</property>

这些测试从STS服务端点请求一组临时凭证。可以在fs.s3a.assumed.role.sts.endpoint中定义备用端点。如果设置了此参数,则还必须在fs.s3a.assumed.role.sts.endpoint.region中定义委托令牌区域。这不仅有助于测试替代基础设施,还能减少远离中心服务执行的测试延迟。

<property>
  <name>fs.s3a.delegation.token.endpoint</name>
  <value>fs.s3a.assumed.role.sts.endpoint</value>
</property>
<property>
  <name>fs.s3a.assumed.role.sts.endpoint.region</name>
  <value>eu-west-2</value>
</property>

默认为"";表示“使用亚马逊默认端点”(sts.amazonaws.com)。

请查阅AWS文档获取完整的位置列表。

禁用内容编码测试

ITestS3AContentEncoding中的测试可能需要禁用

  <property>
    <name>test.fs.s3a.content.encoding.enabled</name>
    <value>false</value>
  </property>

可能失败的测试(可以忽略)

  • ITestS3AContractMultipartUploader 测试 testMultipartUploadAborttestSingleUpload 抛出 FileNotFoundException
  • ITestS3AMiscOperations.testEmptyFileChecksums:如果文件系统始终加密数据。

调试测试失败

在调试级别记录日志是提供更多诊断输出的标准方法;设置后重新运行测试

log4j.logger.org.apache.hadoop.fs.s3a=DEBUG

还有一些用于AWS客户端调试日志记录的日志选项;请查阅该文件。

还可以选择在存储桶上启用日志记录;这或许可用于从该端诊断问题。虽然这不是一个常用的功能,但仍作为一个选项保留。如果被迫采用这种方式进行调试,可以考虑将fs.s3a.user.agent.prefix设置为特定测试运行的唯一前缀,这样能更容易定位特定的日志条目。

添加新测试

我们始终欢迎新的测试。请记住,我们需要控制成本和测试时间,这可以通过以下方式实现

  • 不重复测试。
  • 高效利用Hadoop API调用。
  • 将大型/慢速测试隔离到“scale”测试组中。
  • 设计所有测试以并行执行(在可能的情况下)。
  • 在现有测试中谨慎添加新的探针和谓词。

禁止重复:如果一个操作已在其他测试中验证过,则无需重复测试。这一原则既适用于元数据操作,也适用于批量IO操作。如果新增的测试用例能完全替代现有测试,在确保测试覆盖率不受影响的前提下,可以移除旧测试。

高效: 优先使用getFileStatus()并检查结果,而不是调用exists()isFile()等方法。

隔离式规模测试。任何执行大量IO操作的S3A测试都必须继承S3AScaleTestBase类,这样只有在构建时定义了scale参数才会运行,并支持用户可配置的测试超时设置。规模测试还应支持对象实际大小/操作数量的可配置性,以便验证不同规模下的行为表现。

专为并行执行设计。这里的一个关键需求是每个测试套件都能在文件系统的独立部分上工作。AbstractS3ATestBase的子类应使用path()方法,以测试套件名称为基础路径,来构建隔离路径。测试绝不能假设它们对存储桶拥有独占访问权限。

在适当情况下扩展现有测试。这一建议与常规测试中"每个方法只测试一个功能"的最佳实践相悖。但由于创建目录树或上传大文件的操作极其耗时,我们无法遵循常规做法。所有针对真实S3端点的测试都是集成测试,在这些测试中共享测试设置和拆卸过程可以节省时间和成本。

一种标准做法是通过添加一些额外的断言来扩展现有测试,而不是编写新的测试用例。在进行此操作时,请确保新增的断言在失败时能提供有意义的诊断信息,这样就能从测试日志中轻松调试任何新出现的问题。

在S3A集成测试中高效利用FS实例。 如果测试能复用同一JVM中的现有FS实例,那么使用FileSystem实例的测试速度最快。

如果这样做,你绝对不能关闭或对它们进行唯一配置。如果需要保证100%隔离或具有唯一配置的实例,请创建一个新实例,并且必须在拆卸时关闭该实例以避免资源泄漏。

请勿手动将FileSystem实例(例如通过org.apache.hadoop.fs.FileSystem#addFileSystemForTesting)添加到将在测试运行期间被修改或关闭的缓存中。这可能导致其他测试在使用相同被修改或已关闭的FS实例时失败。更多详情请参阅HADOOP-15819。

新测试需求

这正是我们对新测试的期望;它们是对常规Hadoop需求的扩展,基于需要与远程服务器协作的场景——这些服务器的使用需要密钥凭证,测试可能较慢,并且仅凭测试输出来诊断故障原因至关重要。

子类化现有的共享基类

除非有充分理由,否则请扩展AbstractS3ATestBaseAbstractSTestS3AHugeFiles。这些基类会为对象存储测试做好配置,提供良好的线程命名,帮助生成隔离路径。对于AbstractSTestS3AHugeFiles的子类,只有在设置了-Dscale参数时才会运行。

AbstractS3ATestBase的主要特性

  • getFileSystem() 返回绑定到合约测试文件系统的S3A文件系统,该文件系统定义在fs.s3a.contract.test
  • 如果该URL未设置,将自动跳过所有测试。
  • 扩展 AbstractFSContractTestBaseAssert 的所有方法。

共享基类也有助于减少未来的维护工作。请使用它们。

安全

切勿记录凭证。凭证测试特意避免提供有意义的日志或断言消息,正是为了防止这种情况。

高效节省时间和金钱

这意味着在测试设置/拆卸方面高效,并且理想情况下利用现有的公共数据集以节省设置时间和测试人员成本。

特别值得注意的策略包括:

  1. ITestS3ADirectoryPerformance: 单个测试用例会先设置目录树结构,然后执行不同的列表操作并测量耗时。
  2. AbstractSTestS3AHugeFiles: 将测试套件标记为@FixMethodOrder(MethodSorters.NAME_ASCENDING),然后对测试用例进行排序,使每个测试用例都期望前一个测试已完成(此处指上传文件、重命名文件等操作)。这既能在报告中提供独立的测试结果,又允许执行有序的操作序列。请注意使用Assume.assume()来检测单个测试用例的前提条件是否满足,因此测试会被跳过,而不是因误报的跟踪信息而失败。

AbstractSTestS3AHugeFiles的有序测试用例机制可能是链接测试设置/拆卸的最优雅方式。

关于重用现有数据,我们倾向于使用AWS美国东部区域的noaa-cors-pds存档来测试输入流操作。这不适用于其他区域或第三方S3实现。因此,URL可以被覆盖以在其他地方进行测试。

与其他S3存储兼容

不要假设仅使用AWS S3美国东部区域,需确保能与外部S3实现方案兼容。这些外部实现可能不支持最新的S3 API功能,例如加密、会话API等特性。

它们不会拥有相同的CSV/大型测试文件,因为部分输入测试依赖于这些文件。查看ITestS3AInputStreamPerformance了解如何编写测试以支持在替代文件系统上声明特定的大型测试文件。

适用于长距离链路

除了使文件大小和操作计数可扩展外,这还包括设置适当的测试超时时间。Scale测试使这一配置成为可能;在AbstractS3ATestBase()中硬编码为十分钟;子类可以通过重写getTestTimeoutMillis()来更改此设置。

同样重要的是:支持代理,因为一些测试人员需要它们。

提供诊断和计时信息

  1. 为线程设置有用的名称。
  2. 创建日志,记录信息。要知道S3AFileSystem及其输入输出流全部都在它们的{{toString()}}调用中提供了有用的统计信息;记录这些信息本身就很有价值。
  3. 你可以在这里使用AbstractS3ATestBase.describe(format-stringm, args);它会添加一些换行符以便更容易识别。
  4. 使用ContractTestUtils.NanoTimer来测量操作持续时间,并记录输出日志。

有意义地失败

ContractTestUtils 类包含一整套断言方法,用于对文件系统的预期状态进行声明,例如 assertPathExists(FS, path)assertPathDoesNotExists(FS, path) 等。这些方法会尽力在失败时提供有意义的诊断信息(如目录列表、文件状态等),从而帮助更轻松地理解故障原因。

至少,在使用assertTrue()assertFalse()时不要不包含错误信息。

设置其文件系统并检查这些设置

测试可以重写createConfiguration()方法,以向测试中使用的S3A文件系统实例的配置文件中添加新选项。

然而,文件系统缓存可能导致测试套件获取到一个使用不同配置创建的缓存实例。对于不需要特定配置的测试来说,缓存是有益的:它能减少测试准备时间。

对于那些确实需要独特选项(加密、魔法文件)的测试,可能会出现故障,而且这些故障会以难以复现的方式发生。

在修改配置时,使用S3ATestUtils.disableFilesystemCaching(conf)来禁用缓存。例如来自AbstractTestS3AEncryption的示例:

@Override
protected Configuration createConfiguration() {
  Configuration conf = super.createConfiguration();
  S3ATestUtils.disableFilesystemCaching(conf);
  removeBaseAndBucketOverrides(conf,
      SERVER_SIDE_ENCRYPTION_ALGORITHM);  
  conf.set(Constants.SERVER_SIDE_ENCRYPTION_ALGORITHM,
          getSSEAlgorithm().getMethod());
  return conf;
}

然后在设置方法或测试用例中验证其文件系统是否确实具备所需功能(fs.getConf().getProperty(...))。这不仅能发现文件系统重用问题,还能检测到auth-keys.xml中文件系统配置存在显式的每存储桶设置(这些设置会覆盖测试套件的通用选项配置)的情况。

清理后续工作

降低成本。

  1. 不仅要在测试用例成功完成时进行清理;测试套件的拆卸也必须完成清理。
  2. 在清理之前,该拆卸代码必须检查文件系统和其他字段是否为null。为什么?如果测试设置失败,拆卸方法仍然会被调用。

稳定可靠运行

我们对此深表感激——您也会的。

除非不可行,否则并行运行。

测试必须设计为能与其他测试并行运行,所有测试都使用同一个共享的S3存储桶。这意味着

  • 使用由方法AbstractFSContractTestBase.path(String filepath)提供的相对路径和JVM分支唯一路径。
  • 不操作根目录或对其内容做出断言(例如:删除其内容并断言现在为空)。
  • 对存储桶的所有活跃客户端没有特定要求(例如:SSE-C测试要求所有文件,包括目录标记,都必须使用相同的密钥加密)。
  • Doesn’t use so much bandwidth that all other tests will be starved of IO and start timing out (e.g. the scale tests).

此类测试只能作为顺序测试运行。添加测试时,需先在POM文件中将其从并行failsafe运行中排除,之后再添加到顺序测试中。涉及大量IO操作的测试还必须继承S3AScaleTestBase基类,因此仅当系统/Maven属性fs.s3a.scale.test.enabled为true时才会执行。

可以在集成开发环境(IDE)中运行单个测试用例

这对于调试测试失败非常宝贵。

如何在您的hadoop配置中设置测试选项,而不是在maven命令行上设置:

控制AWS成本

大多数基础的S3测试在设计时都会在测试运行后删除文件,因此您无需支付存储费用。规模测试会处理更多数据,因此成本更高,执行时间通常也更长。

然而您需要支付费用

  1. 测试运行后留在S3中的数据。
  2. 对文件的HTTP操作(HEAD、LIST、GET)。
  3. 来自批量IO或S3A提交器测试的进行中的多部分上传。
  4. 使用AWS KMS密钥进行加密/解密。

每次对文件进行部分读取时都会产生GET/decrypt开销,因此随机IO可能比顺序IO成本更高;但列式数据对查询的加速效果通常能弥补这一开销。

如何降低成本

  • 不要使用大型数据集运行规模测试;保持fs.s3a.scale.test.huge.filesize未设置,或设置为几MB(最小值:5)。
  • 删除文件系统中的所有文件。根测试通常会执行此操作,但也可以手动完成:

    *hadoop fs -rm -r -f -skipTrash s3a://test-bucket/

  • 中止所有未完成的上传:

    hadoop s3guard uploads -abort -force s3a://test-bucket/

提示

如何确保您的凭证真正安全

尽管auth-keys.xml文件在git和subversion中被标记为忽略,但它仍然存在于您的源代码树中,始终存在泄露的风险。

您可以通过将密钥保留在源代码树之外并使用绝对XInclude引用来避免这种情况。

<configuration>

  <include xmlns="http://www.w3.org/2001/XInclude"
    href="file:///users/ubuntu/.auth-keys.xml" />

</configuration>

故障注入

S3A提供了一个“不一致S3客户端工厂”,可用于通过向S3客户端请求注入随机故障来模拟限流。

注意

在之前的版本中,该工厂还可用于在测试S3Guard期间模拟不一致情况。由于现在S3已具备一致性,在测试期间不再需要注入不一致性。

测试假设角色

测试AWS假定角色凭证提供程序需要请求一个假定角色。

如果未在fs.s3a.assumed.role.arn中声明此角色,将跳过需要它的测试。

需要承担角色ARN的具体测试包括

  • ITestAssumeRole.
  • ITestRoleDelegationTokens.
  • ITestDelegatedMRJob中的一个参数化测试用例。

要运行这些测试,您需要:

  1. 在您的AWS账户中,一个角色将拥有对测试中使用的S3存储桶的完全读写访问权限,以及对任何SSE-KMS或DSSE-KMS测试的KMS访问权限。

  2. 您的IAM用户需要拥有“承担”该角色的权限。

  3. 角色ARN必须在fs.s3a.assumed.role.arn中设置。

<property>
  <name>fs.s3a.assumed.role.arn</name>
  <value>arn:aws:iam::9878543210123:role/role-s3-restricted</value>
</property>

测试假设角色拥有不同权限子集,并验证当调用者仅对目录树的部分路径具有写入权限时,S3A客户端(基本)能正常工作。

您也可以通过切换到凭证提供者,以更全面的测试方式,在假设角色下运行整个测试套件。

<property>
  <name>fs.s3a.aws.credentials.provider</name>
  <value>org.apache.hadoop.fs.s3a.auth.AssumedRoleCredentialProvider</value>
</property>

登录存储桶所需的常规凭据仍将被使用,但现在用于与S3交互的凭据将是临时角色凭据,而非完整凭据。

验证AWS SDK更新的资格

更新AWS SDK是一项需要定期执行的任务,但很少能完全避免各种或大或小的复杂问题。

假设SDK版本在X.Y版本发布期间保持不变(不包括安全修复),因此在每个版本发布前进行更新是明智的——只要该更新不会引发任何回归问题。

  1. 不要等到最后一刻才采取行动。
  2. 升级补丁应仅专注于SDK更新,以便可以轻松地挑选和回滚。
  3. 出于同样的原因,不要将SDK更新与其他任何工作混合在一起。
  4. 规划一个下午的工作,包括测试前后、日志分析以及任何手动测试。
  5. 在开始升级之前,确保所有集成测试(包括ARN、加密、规模测试)都正常运行。
  6. 创建一个JIRA来更新SDK。暂时不要包含版本号,因为在准备就绪之前可能需要多次SDK更新。
  7. 识别最新的AWS SDK available for download
  8. 为JIRA创建一个trunk的私有git分支,并在hadoop-project/pom.xml中将aws-java-sdk.version更新为新SDK版本。
  9. 更新NOTICE.txt和LICENSE.binary文件中的AWS SDK版本
  10. 执行一次干净的构建并重新运行所有hadoop-aws测试。这包括-Pscale测试集,其中需要为假设角色测试定义一个角色。在fs.s3a.assumed.role.arn中配置用于测试假设角色的ARN,在fs.s3a.encryption.key中配置加密密钥,以确保完整的测试覆盖范围。如果可能的话,请扩大规模测试的范围。
  11. 为您的存储桶创建一个访问点(使用AWS控制台或CLI),更新S3a配置以使用该访问点(帮助文档),然后从您的IDE或通过maven重新运行ITest*集成测试。
  12. 从您的IDE或通过maven运行ILoadTest*负载测试,使用命令mvn verify -Dtest=skip -Dit.test=ILoadTest\*;既要关注性能退化,也要关注测试失败情况。
  13. 使用mvn site -DskipTests创建站点;报告位于target/site目录中。
  14. 检查hadoop-tools/hadoop-aws/target/failsafe-reports目录下的每一个-output.txt文件,特别注意org.apache.hadoop.fs.s3a.scale.ITestS3AInputStreamPerformance-output.txt文件,因为流关闭/中止逻辑的变更会在这个文件中体现。
  15. 运行mvn install安装构件,然后在hadoop-cloud-storage-project/hadoop-cloud-storage目录下执行mvn dependency:tree -Dverbose > target/dependencies.txt。检查target/dependencies.txt文件,确保没有意外将新构件声明为shaded software.amazon.awssdk:bundle:jar构件的依赖项。
  16. 运行完整的AWS测试套件,通过将fs.s3a.encryption.algorithm设置为'CSE-KMS'并在fs.s3a.encryption.key中配置AWS-KMS密钥ID来启用S3客户端加密。
  17. 验证测试TestAWSV2SDK的输出不包含任何未遮蔽的类。

hadoop-aws 模块的依赖链应该与此类似,尽管版本号可能不同:

[INFO] +- org.apache.hadoop:hadoop-aws:jar:3.4.0-SNAPSHOT:compile
[INFO] |  +- software.amazon.awssdk:bundle:jar:2.23.5:compile
[INFO] |  \- org.wildfly.openssl:wildfly-openssl:jar:1.1.3.Final:compile

基础命令行回归测试

我们需要对CLI进行一次全面检查,看看是否存在导致问题的变更,特别是是否出现了新的日志消息,或者某些打包更改是否破坏了该CLI。

在执行此操作时启用IOStatistics报告总是很有趣

<property>
  <name>fs.iostatistics.logging.level</name>
  <value>info</value>
</property>

从项目根目录开始,创建一个命令行发布版本 mvn package -Pdist -DskipTests -Dmaven.javadoc.skip=true -DskipShade;

  1. 进入 hadoop-dist/target/hadoop-x.y.z-SNAPSHOT 目录。
  2. core-site.xml文件复制到etc/hadoop目录中。
  3. 在命令行或~/.hadoop-env文件中设置HADOOP_OPTIONAL_TOOLS环境变量。
export HADOOP_OPTIONAL_TOOLS="hadoop-aws"

运行一些基本的s3guard命令行工具以及文件操作。

export BUCKETNAME=example-bucket-name
export BUCKET=s3a://$BUCKETNAME

bin/hadoop s3guard bucket-info $BUCKET

bin/hadoop s3guard uploads $BUCKET
# repeat twice, once with "no" and once with "yes" as responses
bin/hadoop s3guard uploads -abort $BUCKET

# ---------------------------------------------------
# root filesystem operatios
# ---------------------------------------------------

bin/hadoop fs -ls $BUCKET/
# assuming file is not yet created, expect error and status code of 1
bin/hadoop fs -ls $BUCKET/file

# exit code of 0 even when path doesn't exist
bin/hadoop fs -rm -R -f $BUCKET/dir-no-trailing
bin/hadoop fs -rm -R -f $BUCKET/dir-trailing/

# error because it is a directory
bin/hadoop fs -rm $BUCKET/

bin/hadoop fs -touchz $BUCKET/file
# expect I/O error as it is the root directory
bin/hadoop fs -rm -r $BUCKET/

# succeeds
bin/hadoop fs -rm -r $BUCKET/\*

# ---------------------------------------------------
# File operations
# ---------------------------------------------------

bin/hadoop fs -mkdir $BUCKET/dir-no-trailing
bin/hadoop fs -mkdir $BUCKET/dir-trailing/
bin/hadoop fs -touchz $BUCKET/file
bin/hadoop fs -ls $BUCKET/
bin/hadoop fs -mv $BUCKET/file $BUCKET/file2
# expect "No such file or directory"
bin/hadoop fs -stat $BUCKET/file

# expect success
bin/hadoop fs -stat $BUCKET/file2

# expect "file exists"
bin/hadoop fs -mkdir $BUCKET/dir-no-trailing
bin/hadoop fs -mv $BUCKET/file2 $BUCKET/dir-no-trailing
bin/hadoop fs -stat $BUCKET/dir-no-trailing/file2
# treated the same as the file stat
bin/hadoop fs -stat $BUCKET/dir-no-trailing/file2/
bin/hadoop fs -ls $BUCKET/dir-no-trailing/file2/
bin/hadoop fs -ls $BUCKET/dir-no-trailing
# expect a "0" here:
bin/hadoop fs -test -d  $BUCKET/dir-no-trailing ; echo $?
# expect a "1" here:
bin/hadoop fs -test -d  $BUCKET/dir-no-trailing/file2 ; echo $?
# will return NONE unless bucket has checksums enabled
bin/hadoop fs -checksum $BUCKET/dir-no-trailing/file2
# expect "etag" + a long string
bin/hadoop fs -D fs.s3a.etag.checksum.enabled=true -checksum $BUCKET/dir-no-trailing/file2
bin/hadoop fs -expunge -immediate -fs $BUCKET

# ---------------------------------------------------
# Delegation Token support
# ---------------------------------------------------

# failure unless delegation tokens are enabled
bin/hdfs fetchdt --webservice $BUCKET secrets.bin
# success
bin/hdfs fetchdt -D fs.s3a.delegation.token.binding=org.apache.hadoop.fs.s3a.auth.delegation.SessionTokenBinding --webservice $BUCKET secrets.bin
bin/hdfs fetchdt -print secrets.bin

# expect warning "No TokenRenewer defined for token kind S3ADelegationToken/Session"
bin/hdfs fetchdt -renew secrets.bin


# ---------------------------------------------------
# Copy to from local
# ---------------------------------------------------

time bin/hadoop fs -copyFromLocal -t 10  share/hadoop/tools/lib/*aws*jar $BUCKET/

# expect the iostatistics object_list_request value to be O(directories)
bin/hadoop fs -ls -R $BUCKET/

# expect the iostatistics object_list_request and op_get_content_summary values to be 1
bin/hadoop fs -du -h -s $BUCKET/

mkdir tmp
time bin/hadoop fs -copyToLocal -t 10  $BUCKET/\*aws\* tmp

# ---------------------------------------------------
# Cloudstore
# check out and build https://github.com/steveloughran/cloudstore
# then for these tests, set CLOUDSTORE env var to point to the JAR
# ---------------------------------------------------

bin/hadoop jar $CLOUDSTORE storediag $BUCKET

time bin/hadoop jar $CLOUDSTORE bandwidth 64M $BUCKET/testfile

其他测试

  • 无论您有哪些使用S3A的应用程序:在升级前构建并运行它们。然后观察升级完成后它们是否能在大致相同的时间内成功完成。
  • 测试您有权访问的任何第三方端点。
  • 尝试不同的区域(特别是仅支持IPv4的区域)和加密设置。
  • 您进行的任何性能测试都可以识别出速度下降的情况,这可能是SDK行为发生变化的信号(特别是在流读取和写入时)。
  • 如果可能,尝试在需要通过代理与AWS服务通信的环境中进行测试。
  • 尝试让其他人,特别是那些拥有自己终端、应用或不同部署环境的人,运行他们自己的测试。
  • 运行负载测试,尤其是ILoadTestS3ABulkDeleteThrottling
  • 检出cloudstore,针对您的hadoop版本进行构建,然后使用其CLI运行一些命令(storediag等)

处理已弃用的API和新功能

Jenkins运行会告诉你是否有新的弃用内容。如果有,你应该考虑如何处理它们。

迁移到之前SDK版本中不存在的方法和API,使得在出现问题时更难回滚;但弃用这些内容可能有充分的理由。

与此同时,继续使用旧代码可能有充分的理由。

  • AWS采用了构建器模式来创建新操作;请注意,以此方式构建的对象通常会禁用其(现有的)setter方法;这可能会破坏现有代码。
  • S3调用的新版本(列表v2、存储桶存在性检查、批量操作)可能比之前的HTTP操作和API更好,但它们可能无法与第三方端点兼容,因此只有在可选的情况下才能采用,这又会增加一个新的配置选项(包括文档、测试等)。此类变更必须在其自己的补丁中完成,并附带比较新旧操作的新测试。

提交补丁

当补丁提交时:将JIRA更新为实际使用的版本号;在提交信息中使用该标题。

准备好回滚、重新迭代或编写代码以解决回归问题。

随着更广泛的使用,可能会出现一些问题,这些问题可以在新的AWS版本中得到修复,回滚到旧版本,或者通过HADOOP-14596进行解决。

如果发生这种情况,请不要惊讶,也不必过于担心。虽然回滚选项可供使用,但理想情况下应尽量向前推进。

如果问题出在SDK上,请在AWS V2 SDK Bug tracker提交问题。如果问题可以通过Hadoop代码修复或解决,也请在那里进行处理。