- OpenTofu 内部机制
- 远程服务发现
远程服务发现
OpenTofu 使用远程服务实现了其大部分功能。虽然在许多情况下,这些都是对许多应用程序有用的通用第三方服务,但其中一些服务是专门针对 OpenTofu 的需求而定制的。我们将这些服务称为“OpenTofu 原生服务”,OpenTofu 通过下面描述的远程服务发现协议与它们进行交互。
面向用户的主机名
从用户的角度来看,OpenTofu 原生服务是在一个面向用户的“友好主机名”下提供的,该主机名作为配置和任何所需身份验证凭据的关键。
发现协议的目的是将用户提供的主机名映射到特定服务的基准 URL。每个主机都可以提供不同组合的服务——或者根本不提供服务!——因此,发现协议的第二个目的是允许 OpenTofu 识别给定主机名下哪些服务是有效的。
例如,模块源字符串可以包含模块注册表主机名作为其第一个片段,例如 example.com/namespace/name/provider
,OpenTofu 使用服务发现来确定 example.com
是否具有模块注册表,以及如果存在,其 API 在哪里可用。
面向用户的主机名是使用其 Unicode 形式表示的完整指定的国际化域名(不允许使用相应的“Punycode”形式),并且必须能够在 DNS 中解析到在端口 443 上运行 HTTPS 服务器的地址。
使用标准 Unicode Nameprep 算法对面向用户的主机名进行规范化以进行内部比较,其中包括将所有字母转换为小写,尽可能将组合变音符号规范化为预组合形式,以及其他各种规范化步骤。
发现过程
给定一个主机名,发现过程首先使用该主机名以及 https:
方案和固定路径 /.well-known/terraform.json
形成一个初始发现 URL。
例如,给定主机名 example.com
,初始发现 URL 将为 https://example.com/.well-known/terraform.json
。
然后,OpenTofu 向此发现 URL 发送 GET
请求,并期望收到 JSON 响应。如果响应的状态不是 200,没有 application/json
的媒体类型,或者如果主体无法解析为 JSON 对象,则发现失败,OpenTofu 认为主机不支持任何 OpenTofu 原生服务。
如果响应是 HTTP 重定向,则 OpenTofu 使用新位置作为其发现 URL 重复此步骤。OpenTofu 保证至少遵循一个重定向,但不保证也不推荐嵌套重定向。
如果响应是有效的 JSON 对象,则其键是 OpenTofu 原生服务标识符,由服务类型名称和版本字符串用句点分隔组成。例如,模块注册表协议版本 1 的服务标识符为 modules.v1
。
每个对象元素的值是相关服务的基准 URL。此 URL 可以是绝对的或相对的,如果是相对的,则相对于最终发现 URL(在遵循重定向之后)解析。
以下是一个声明支持模块注册表协议版本 1 的发现文档示例
{
"modules.v1": "https://modules.example.com/v1/"
}
支持的服务
目前,以下服务标识符正在使用中
login.v1
: 登录协议版本 1modules.v1
: 模块注册表 API 版本 1providers.v1
: 提供程序注册表 API 版本 1
身份验证
如果给定主机名的凭据通过 credentials_helper
或特定于主机名的环境变量在 CLI 配置 中可用,则它们将包含在发现文档的请求中。
根据相关服务的需要,凭据也可以提供给发现文档中声明的端点。
用户可见主机名中的非标准端口
强烈建议在标准 HTTPS 端口 443 上提供主机名的发现文档。但是,在开发环境中,这并不总是可行或方便的,因此 OpenTofu 允许主机名以冒号后跟一个或多个十进制数字组成的端口规范结尾。
当存在自定义端口号时,预期该端口上的服务将实现 HTTPS 并响应相同的固定发现路径。
对于日常使用,强烈建议不要依赖此机制,而是应在标准端口上提供发现文档,因为这允许使用最友好的主机名形式。