
floci Resource Explorer 2 实战指南在本地模拟 AWS 跨服务资源搜索、索引与视图管理【免费下载链接】flociLight, fluffy, and always free - The AWS Local Emulator alternative项目地址: https://gitcode.com/gh_mirrors/fl/flocifloci 的 Resource Explorer 2服务命名空间resource-explorer-2把其他 floci 服务持有的资源汇聚到一处进行检索Search与ListResources能在所有已启用服务上执行 Resource Explorer 查询语言索引与视图管理则与 AWS 保持一致的行为契约。本文以 docs/services/resource-explorer.md 为主线结合 ResourceExplorer2Service.java 等核心实现完整覆盖 API 清单、索引/视图生命周期、如何让一个服务可被发现、完整查询语法、配置项、与真实 AWS 的差异以及路由细节读完即可在本地用 AWS CLI / SDK 直接搜索你创建的各类云资源。服务形态与接入方式Resource Explorer 2 是一个 REST JSON 服务操作名直接作为 URI 路径例如POST /Search与 AWS 的resource-explorer-2API 对应。floci 将它和其他服务一样暴露在共享端口 4566 上因此本地只需要把 AWS CLI / SDK 的 endpoint 指向http://localhost:4566、区域与账号按 floci 的默认值配置即可调用无需额外端口。Controller 层在 ResourceExplorer2Controller.java 中实现全部端点Path(/)、请求与响应均为 JSON。每个操作对MaxResults有独立的上限与 AWS 保持一致越界会收到ValidationException操作分组MaxResults 上限ListResources/Search/ListSupportedResourceTypes1000ListIndexes/ListServiceIndexes/GetResourceExplorerSetup100ListViews/ListManagedViews/ListServiceViews/ListStreamingAccessForServices50ListIndexesForMembers10支持的操作下表是 floci 完整实现的 Resource Explorer 2 操作来自 docs/services/resource-explorer.md 的动作清单与 Controller 中的端点一一对应ActionDescriptionListResourcesSearch with filters only; pages through every matchSearchSearch with filters and free-form keywords; returns at most the first 1000 matchesListSupportedResourceTypesList the resource types the enabled services exposeCreateIndexTurn Resource Explorer on in a RegionGetIndexDetails of the Regions indexDeleteIndexTurn Resource Explorer off in a RegionListIndexesList indexes, optionally by type or RegionUpdateIndexTypePromote a local index to aggregator, or demote itCreateViewCreate a view, optionally with a filter and tag dataGetViewA views definition and tagsDeleteViewDelete a view, clearing it as a default if it was oneUpdateViewReplace a views filter or included propertiesListViewsARNs of the calling Regions viewsBatchGetViewSeveral views at once, reporting the ones that are missingAssociateDefaultViewMake a view the Regions defaultDisassociateDefaultViewLeave the Region without a default viewGetDefaultViewThe Regions default view ARN, if it has oneGetAccountLevelServiceConfigurationOrganization status — alwaysDISABLED, as no organization is modeledListIndexesForMembersMember accounts indexes — this accounts own, or emptyListManagedViewsAWS-managed views — always empty, as none are modeledGetManagedViewAlways reports the managed view as not foundListServiceViewsService-owned views — always empty, as none are modeledGetServiceViewAlways reports the service view as not foundListServiceIndexesIndexes across Regions, in the service-index shapeGetServiceIndexThe Regions index ARN and typeListStreamingAccessForServicesServices granted streaming access — always empty, as none are modeledCreateResourceExplorerSetupCreate indexes and views across several Regions in one taskDeleteResourceExplorerSetupRemove indexes and views from named Regions, or from all of themGetResourceExplorerSetupA setup tasks per-Region index and view statusListTagsForResourceAn indexs or views tagsTagResourceAdd or overwrite tags on an index or viewUntagResourceRemove tags by key from an index or view需要留意几个在实现层面被“固定为空/固定报错”的表面floci 不建模 AWS Organizations也不建模受信任服务trusted service集成所以组织级与托管视图类操作返回的是线协议正确wire-accurate的空结果而不是错误——调用方枚举它们可以正常完成按具体 ARN 查询时得到的ResourceNotFoundException也与真实账户中没有此类视图时的行为一致。相关实现集中在 ResourceExplorer2Service.java 的 “Managed, service-owned, and organization-scoped reads” 区域。索引与视图开箱即用与多区域编排索引Index决定一个 Region 是否启用了 Resource Explorer视图View决定一次搜索能返回哪些资源、以及是否带回标签数据。floci 与 AWS 的关键差异在于本地开箱即用启动时onStartup监听 QuarkusStartupEvent见 ResourceExplorer2Service.java自动在默认 Region 预置一个AGGREGATOR索引和一个名为default-view的视图因此无需任何配置Search就能直接工作。其他 Region 需要先执行CreateIndex或通过CreateResourceExplorerSetup批量创建。自动预置逻辑ensureDefaultRegionProvisioned有几个值得注意的细节它以“是否存在ACTIVE索引”为守卫而非“存储是否为空”持久化存储重启后索引仍在则不再重复预置显式DisassociateDefaultView的结果在重启后依然有效该逻辑不仅在启动时运行还在每次需要基线GetIndex、GetDefaultView、Search、ListResources等前懒执行这是为了应对 LocalStack 风格state/reset在进程内清空存储见EmulatorInfoController而无需重启进程的场景预置的默认视图自带IncludedProperty(name tags)即默认视图就允许 tag 相关过滤。索引的创建、删除与类型CreateIndex在 floci 中同步创建LOCAL类型索引服务端存储为ACTIVE但 Controller 在创建响应中按 AWS API 契约回State: CREATING见 ResourceExplorer2Controller.java——因此紧随其后的GetIndex会报告ACTIVE这与真实 AWS 存在“预置窗口”不同。同一 Region 已有索引时CreateIndex抛ConflictException409。DeleteIndex按 ARN 删除若删除后已无任何索引会顺带清空所有视图与默认视图记录。UpdateIndexType把索引在LOCAL与AGGREGATOR之间切换AWS 规定一个账号至多一个 aggregator 索引所以当别的 Region 已存在AGGREGATOR索引时会抛ConflictException见 ResourceExplorer2Service.java。视图的生命周期CreateView的ViewName必须匹配[a-zA-Z0-9-]{1,64}常量VIEW_NAME_PATTERN同 Region 内重名抛ConflictExceptionIncludedProperties目前只接受名为tags的属性其他名称抛ValidationExceptionvalidateIncludedProperties。Scope缺省为当前账号的arn:aws:iam::account:root。AssociateDefaultView强制校验“视图 ARN 的 Region 必须与请求 Region 一致”否则抛ValidationException与 AWS 的分 Region 默认视图语义一致ResourceExplorer2Service.java。删除默认视图时该 Region 直接失去默认视图AWS 允许此行为DeleteView会清理默认视图记录。视图的“是否携带标签数据”由View.includesTags()判定View.javaSearch/ListResources构造响应时据此决定是否把Properties中的tags数据带出。多区域编排CreateResourceExplorerSetupCreateResourceExplorerSetup一次调用即可在多个 Region 各建一个索引 一个视图并可把其中至多一个 Region 提升为 aggregator常量MAX_AGGREGATOR_REGIONS 1随后返回一个TaskId供GetResourceExplorerSetup回读。校验规则包括RegionList至少一项、Region 名必须匹配[a-z-]-[a-z]-[0-9]、AggregatorRegions必须同时出现在RegionList中、ViewName同样受 1-64 字符约束。每个 Region 的索引步骤与视图步骤独立记录某个 Region 失败例如视图重名、aggregator 已在别处时通过该 Region 的ErrorDetails报告 AWS 错误码而不会放弃任务中的其他 RegionsetUpRegion/tearDownRegion返回SetupOutcome见 ResourceExplorer2Service.java。DeleteResourceExplorerSetup支持按RegionList删除或DeleteInAllRegions: true全量关闭两者同时给出会抛ValidationException。让一个服务可被发现ResourceProvider 扩展点任何 floci 服务要暴露自己的资源只需实现核心扩展点 core/resource/ResourceProvider.javapublic interface ResourceProvider { ListExplorerResource getResources(); SetSupportedResourceType getSupportedResourceTypes(); }实现类必须是 CDI Bean通常是ApplicationScoped的服务类ResourceExplorer2Service通过InstanceResourceProvider收集全部实现。getResources()应从实时存储扫描并映射为ExplorerResource而不是缓存返回空列表即可绝不能返回null。顺序无意义——分页前会按 ARN 排序。getSupportedResourceTypes()每个资源类型返回一个SupportedResourceType类型字符串必须与getResources()中ExplorerResource.resourceType()完全一致且全局唯一否则会出现“资源可搜但ListSupportedResourceTypes里看不到它”这种与 AWS 不一致的行为。单个资源的数据载体是 core/resource/ExplorerResource.javarecord字段即查询语言匹配的对象字段含义arn完整 ARN同时充当id:过滤器匹配对象与关键词匹配对象resourceTypeservice:subtype形式的类型标识如acm:certificate、kafka:clusterserviceARN 的服务段命名空间如acm、kafka、cognito-idpregion资源所在区域通常取AwsArnUtils.parse(arn).region()owningAccountId12 位账号 ID通常取AwsArnUtils.parse(arn).accountId()lastReportedAt最近观测时间floci 无扫描管道provider 一般传资源创建时间未知时回退Instant.now()tagskey→value 标签驱动全部 tag 过滤器并作为tags属性输出不能为 null无标签传Map.of()没有真正的索引每次Search/ListResources都会调用每个 provider 的getResources()重新聚合所以结果永远与服务的实时状态一致。代价是 provider 必须“便宜”并且必须容忍正在创建中的资源——跳过 null ARN把 null 标签映射视为空。另外paginateResources对单个 provider 的异常做了隔离某个 provider 抛RuntimeException时只跳过它并记录告警日志不会让整个搜索 500best-effort 语义见 ResourceExplorer2Service.java。实现参考LambdaService是最短的示例Ec2Service最大、映射了八种资源类型provider 接口的 Javadoc 还推荐了AcmService、MskService、IamService暴露两种类型作为参照实现。当前可被发现的资源类型来自 docs/services/resource-explorer.md 的完整清单ServiceResource typesACMacm:certificateAmazon MQmq:brokerCloudFormationcloudformation:stackCloudWatch Logslogs:log-groupCognitocognito-idp:userpoolDynamoDBdynamodb:tableEC2ec2:instance,ec2:vpc,ec2:subnet,ec2:security-group,ec2:volume,ec2:internet-gateway,ec2:natgateway,ec2:route-tableECRecr:repositoryECSecs:cluster,ecs:serviceEKSeks:clusterElastiCacheelasticache:clusterELB v2elasticloadbalancing:loadbalancer,elasticloadbalancing:targetgroupEventBridgeevents:event-bus,events:ruleFirehosefirehose:deliverystreamIAMiam:user,iam:roleKinesiskinesis:streamKMSkms:keyLambdalambda:functionLightsaillightsail:instance,lightsail:database,lightsail:bucketMSKkafka:clusterOpenSearches:domainPipespipes:pipeRDSrds:dbRUMrum:appmonitorS3s3:bucketSecrets Managersecretsmanager:secretSNSsns:topicSQSsqs:queueSSMssm:parameterStep Functionsstates:statemachineAPI Gateway 与 Route 53 目前尚不可被发现各自原因不同RestApi的 ARN 不携带 Region而Route53Service解析不到账号 IDhosted zone 的 ARN 两者都没有。两个服务都是在等一个字段而不是缺新的查询支持。查询语法完整解析与匹配语义过滤语法在 query/QueryParser.java 中手工逐字符实现支持完整语法集过滤器accountid:、application:、id:、region:、resourcetype:、resourcetype.supports:、service:、tag:、tag.key:、tag.value:特殊值tag:all有任意标签、tag:none无任何标签结构能力同一过滤器内逗号分隔的值是ORregion:us-east-1,eu-west-1前缀-表示否定值尾部*为前缀匹配双引号包裹短语反斜杠\转义解析器对转义的顺序处理是精确性的关键转义在“决定字符含义”的位置才被解析——拆分过滤器逗号分隔值时、寻找分隔前缀与值的冒号时、识别尾部通配符时。正是这种顺序让tag.key:comma\,literal是一个值而不是两个也让my\-key\-word是关键词而非否定。引号内出现的操作符字符* - : \ ,会被自动转义为字面量。匹配语义在 query/ResourceFilter.java 中实现过滤器之间是 AND同一过滤器内多个值是 OR——与 AWS 文档一致字符串比较大小写不敏感*前缀匹配等价于startsWithapplication:读取资源上的awsApplication标签AWS 既接受应用资源组 ARN 也接受名称因此matchesApplication会同时比较标签原值ARN和从arn:aws:resource-groups:...:group/MyApp/...中抽取的应用名resourcetype.supports:tags基于 provider 上报的SupportedResourceType.supportsTags()汇总集合判断自由文本关键词是 OR 语义ec2 billing匹配任一的资源匹配目标是 ARN、资源类型、服务、Region 以及标签键值大小写不敏感的包含测试纯否定查询全部为-keyword匹配“未被排除的一切”。ListResources 与 Search 的契约差异ListResources只接受过滤器一旦出现自由文本关键词parseFilterOnly直接拒绝Controller 将其转为ValidationException400——这与 AWS 的行为一致它没有总结果上限会翻页遍历所有匹配内部用UNLIMITED_RESULTS Integer.MAX_VALUE作为哨兵区分于 Search 的契约。Search同时接受过滤器与关键词但至多返回前 1000 条匹配常量MAX_RESOURCE_RESULTS 1000对齐 AWS “The operation can return only the first 1,000 results”并在Count里给出TotalResources与Complete匹配总数是否 ≤ 1000。ResourceExplorer2ListVsSearchCapTest.java 专门验证了这一差异——历史上曾因硬编码同一个上限而让ListResources也悄悄丢掉 1000 条之后的匹配。标签过滤依赖视图配置tag:、tag.key:、tag.value:以及读取标签的application:都要求视图的IncludedProperties包含名为tags的项。对不含标签数据的视图执行这类查询会被拒绝ValidationException提示需要给视图加tags属性而不是在视图不允许暴露的数据上悄悄求值rejectTagFilterWithoutTagData见 ResourceExplorer2Service.java且无论是否带标签数据搜索结果中是否输出Properties的 tags 都由视图决定。配置项VariableDefaultDescriptionFLOCI_SERVICES_RESOURCEEXPLORER2_ENABLEDtrueEnable or disable Resource Explorer 2FLOCI_STORAGE_SERVICES_RESOURCEEXPLORER2_MODE(global storage mode)Override the storage mode for indexes, views, and setup tasks第二个变量控制索引、视图、默认视图与 setup 任务的存储后端。从源码看这些数据通过StorageFactory按固定文件名持久化ResourceExplorer2Service.javaresourceexplorer2-indexes.json、resourceexplorer2-views.json、resourceexplorer2-default-views.json、resourceexplorer2-setup-tasks.json四者独立存储因此默认视图的选择DisassociateDefaultView的结果能跨重启存活——这一点由 ResourceExplorer2DefaultViewPersistenceTest.java 覆盖该测试通过共享存储后端模拟“重启后持久状态保留、内存状态丢失”。floci 与 AWS 的差异自由文本关键词是“筛选”而非“排序”AWS 把ec2 billing读作ec2 OR billing并且不会因为资源缺少某个关键词而排除它——只会降低排序仍返回它所以真实账户里纯关键词搜索会返回所有资源。floci 保留 OR 语义但只返回匹配项因为没有相关性模型且无过滤的全量结果在本地没有实用价值。过滤器AWS 文档明确为确定性匹配则精确匹配。索引立即ACTIVECreateIndex在自身响应里按 API 契约返回CREATING但 floci 是同步实现、没有可伪装的预置窗口所以紧接着的GetIndex就报告ACTIVE。Setup 任务在调用返回前已完成CreateResourceExplorerSetup/DeleteResourceExplorerSetup同步执行因此第一次GetResourceExplorerSetup读到的各 Region 状态就已是终态SUCCEEDED或FAILED。过期的NextToken返回空页令牌指向的结果集在两次调用之间缩水时返回空页而不是报错——这正好对应 AWS 对重放令牌的处理方式pageBounds会把解码出的偏移量clamp到当前结果集大小内解码失败则抛ValidationException见 ResourceExplorer2Service.java。分页结果按 ARN 排序AWS 把NextToken视为不透明值且不承诺结果顺序Search是相关性排序。floci 的令牌是“每次调用由 provider 与存储迭代重建的列表”的偏移量两者都无序所以切页前必须先按 ARN 排序Comparator.comparing(ExplorerResource::arn)否则第二页会索引到排列不同的列表悄悄丢资源或重复资源。ResourceExplorer2PaginationOrderingTest.java 专门守护这一不变量。同一排序同样适用于ListIndexes、ListViews、ListSupportedResourceTypes。默认视图是持久化的而非推断的每个 Region 的默认视图独立持久化不依赖视图名推导因此DisassociateDefaultView在重启后依然生效。IAM 标签被索引AWS 不索引 IAM 用户和角色上的标签floci 会索引否则本地打标的角色将无法通过标签找到且没有任何收益。组织与受信任服务表面为空见“支持的操作”一节——ListManagedViews、ListServiceViews、ListStreamingAccessForServices返回空列表GetManagedView/GetServiceView报ResourceNotFoundExceptionGetAccountLevelServiceConfiguration报AWSServiceAccessStatus: DISABLEDListIndexesForMembers在请求的账号 ID 中包含本账号时返回本账号的索引否则为空。此外还有一个值得注意的翻页细节ListResources在MaxResults设为 1000 时不生成NextToken——这复现了 AWS 文档中的怪癖The ListResources operation does not generate a NextToken if you set MaxResults to 1000即调用方在最大页大小时会静默拿到至多 1000 条结果且无法继续翻页listResourcesEmitsToken见 ResourceExplorer2Service.java。路由说明与 S3 Vectors 的路径消歧Resource Explorer 2 与 S3 Vectors 都是根路径/下的 REST JSON 服务并且都声明了/CreateIndex、/GetIndex、/ListIndexes、/DeleteIndexS3 Vectors 侧还重叠了/ListResources。JAX-RS 无法仅凭路径区分这两个服务因此 ResourceExplorer2PathRewriteFilter.java 作为PreMatching过滤器介入它对上述歧义路径从请求头Authorization中解析SigV4 凭据范围credential scope并通过ResolvedServiceCatalog反查服务身份——这与AwsQueryController区分 Query 协议服务用的是同一信号凭据范围解析为resource-explorer-2时把路径重写为/re2/Path前缀路由到ResourceExplorer2Controller的对应端点如/re2/CreateIndex否则放行给 S3 Vectors其余 Resource Explorer 2 路径如/ListResources、/Search在各 Controller 间是唯一的无需重写。实际影响不带Authorization头的请求会落到 S3 Vectors。所有 AWS SDK 与 CLI 都会签名请求所以这只影响手工编写的裸curl调用——用 CLI/SDK 时完全无感。本地快速验证以 AWS CLI 为例endpoint 指向http://localhost:4566# 查看默认 Region 自动预置的索引无需任何前置步骤 aws resource-explorer-2 get-index --region us-east-1 --endpoint-url http://localhost:4566 # 搜索所有 Lambda 函数 aws resource-explorer-2 search --query-string resourcetype:lambda:function \ --endpoint-url http://localhost:4566 # 只带过滤器分页枚举ListResources 不接受自由文本 aws resource-explorer-2 list-resources --filters FilterStringservice:s3 \ --endpoint-url http://localhost:4566 # 跨区域一次开通 aws resource-explorer-2 create-resource-explorer-setup \ --region-list us-east-1 eu-west-1 \ --view-name myview \ --aggregator-regions us-east-1 \ --endpoint-url http://localhost:4566总体而言floci 的 Resource Explorer 2 是一个“零配置即可用、语义对齐 AWS、扩展点清晰”的本地资源发现服务新增一个可搜索的服务只需实现一个 CDI Bean 接口查询语法完整覆盖过滤器、关键词、标签与多区域编排而对 AWS 的每一处差异都有明确的取舍理由测试套件ResourceExplorer2IntegrationTest.java、分页、上限与默认视图持久化等则为这些行为提供了回归保障。【免费下载链接】flociLight, fluffy, and always free - The AWS Local Emulator alternative项目地址: https://gitcode.com/gh_mirrors/fl/floci创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考