Dagger GraphQL API 详解:http 查询如何把远程 URL 变成可缓存的文件对象
2026/9/14 1:18:34 网站建设 项目流程

Dagger GraphQL API 详解:http 查询如何把远程 URL 变成可缓存的文件对象

【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger

Dagger 的 GraphQL API 中,http是一个把远程 HTTP(S) URL 直接拉取为 Dagger 文件对象(File)的顶层查询。本文以 GraphQL API 参考文档中的标准示例查询docs/docs-graphql/data/examples/queries/http/gql.md为主体,结合 schema 定义、核心实现 与 集成测试,完整讲解该查询的字段、全部可选参数(urlnamepermissionschecksumauthHeaderexperimentalServiceHost)、底层"快照缓存 + ETag/Last-Modified 增量校验"机制,以及它在构建流水线中的实际用法。读完后你既能读懂这条 GraphQL 查询,也能理解 Dagger 引擎是如何保证下载可缓存、可校验、可复用的。

标准示例查询:一次请求,拿到文件与内容

GraphQL API 参考文档中为http查询提供的标准示例如下(来自 示例文件,该文件由 spectaql 示例加载脚本 按查询名data/examples/queries/http/gql.md读取并渲染到 API 参考站点中):

query { http(url: "http://dagger.io") { size contents } }

这条查询做三件事:

  1. Query根对象调用http字段,传入url参数"http://dagger.io"
  2. 得到返回的File对象(Dagger 中表示单个文件的对象类型);
  3. 选出该文件的两个标量字段:
    • size:文件字节数(Int);
    • contents:文件全文内容(String)。

在 schema 定义 中,http字段的官方文档描述是"Returns a file containing an http remote url content."(返回一个包含远程 HTTP URL 内容的文件),注册方式是标准的 dagql 节点字段:

dagql.Fields[*core.Query]{ dagql.NodeFunc("http", s.http). WithInput(dagql.PerSessionInput). Doc(`Returns a file containing an http remote url content.`). Args( dagql.Arg("url").Doc(`HTTP url to get the content from (e.g., "https://docs.dagger.io").`), dagql.Arg("name").Doc(`File name to use for the file. Defaults to the last part of the URL.`), dagql.Arg("permissions").Doc(`Permissions to set on the file.`), dagql.Arg("checksum").Doc(`Expected digest of the downloaded content (e.g., "sha256:...").`), dagql.Arg("authHeader").Doc(`Secret used to populate the Authorization HTTP header`), dagql.Arg("experimentalServiceHost").Doc(`A service which must be started before the URL is fetched.`), ), }.Install(srv)

注意WithInput(dagql.PerSessionInput)http的输入是按会话(session)划分的,这直接对应了后文的"每个会话独立缓存"行为。

返回对象:File上可以选哪些字段

示例中选了sizecontents,但http返回的是完整的File对象,因此你还可以继续选择其他常用字段:

query { http(url: "https://raw.githubusercontent.com/dagger/dagger/main/LICENSE") { name # 文件名(默认取 URL 路径的最后一部分) size # 字节数 contents # 文本内容 directory # 父目录对象,可用于放入容器/目录 checksum # 文件内容摘要 id # 持久化对象 ID } }

其中name字段的取值规则在 httpPath 函数 中有明确实现:

  • 若显式传了name参数,直接使用;
  • 否则解析 URL,取url.Parse后路径的filepath.Base
  • 若解析出来是空串、./(例如 URL 以/结尾),则回退为"index"

集成测试 TestHTTPName 验证了这一点:拉取.../README.md时文件名为README.md,而传HTTPOpts{Name: "FooBar.md"}后文件名为FooBar.md

完整参数表与行为说明

结合 schema 注册代码 与 httpArgs 结构体,http的全部参数如下:

参数类型说明默认值 / 备注
urlString!要拉取的 HTTP(S) URL必填
nameString文件的名称默认取 URL 路径最后一段(见上节规则)
permissionsInt设置到文件上的权限缺省为0600,见 默认值代码
checksumString期望的内容摘要,如sha256:...可选;不匹配时报http checksum mismatch错误
authHeaderSecretID用于填充Authorization请求头的 Secret可选
experimentalServiceHostServiceID在拉取 URL 之前必须先启动的服务可选;用于访问引擎内服务网络

几点值得注意的实现细节:

  • 默认权限 0600:http 解析器 中permissions := int(args.Permissions.GetOr(dagql.Int(0600))),即不指定时文件以0600权限落盘。TestHTTPPermissions 用stat -c %a验证了显式传入0765/0764后权限确实生效。
  • checksum 是 OpenContainers digest 语法:参数经 parseChecksumArg 解析,即必须是sha256:...这类合法 digest 格式,否则报错invalid checksum "..."(单测见 TestParseChecksumArg)。内容实际摘要在 核心实现 中用sha256流式计算(io.MultiWriter(f, h)),不匹配时返回http checksum mismatch: expected ..., got ...
  • 版本可见性checksum与内部_httpState字段在 schema 上标注了View(AfterVersion("v0.21.0")),即客户端需要 v0.21.0 及以上版本才能看到/使用这些能力(见 schema 安装代码)。
  • authHeader 走 Secret 而非明文AuthHeader的类型是core.SecretID,解析器会先加载 Secret 再取其Plaintext填入请求头(resolveHTTPSessionContext),避免把凭据写进查询文本。TestHTTPAuth 展示了典型场景:服务端要求 Basic Auth,不带authHeader时返回401 Unauthorized,带c.SetSecret(...)之后才能取到内容。

底层机制一:普通拉取路径(FetchHTTPFile)

当不涉及authHeader/experimentalServiceHost时,请求会走引擎内部的 HTTP 状态缓存路径(见下节);而当传了这两个参数时,http 解析器 会切换到直接拉取路径core.FetchHTTPFile。该函数(core/http.go)的流程是:

  1. 构造GET请求,强制Accept-Encoding: identity(禁用压缩,便于按字节流落盘并计算摘要);若提供AuthorizationHeader则设置Authorization头;
  2. 通过doHTTPClientRequest发起请求——它使用基于引擎 DNS 配置的传输层(netconfhttp.NewTransport+DNSConfig),并对非 2xx/3xx 状态码直接报invalid response status ...错误(状态码校验);
  3. 在快照(snapshot)中创建一个临时文件,把响应体一边写入文件、一边喂给 sha256 哈希;
  4. 文件时间戳处理:若响应带Last-Modified头,则把它解析后写为文件 mtime(os.Chtimes),没有则回退到 Unix epoch(时间戳代码)。TestHTTPTimestamp 专门验证了"文件 mtime = Last-Modified 头"这一行为;
  5. 提交快照(bkref.Commit),校验 checksum,最终包装成File对象(持有快照引用与文件名),返回给调用方。

由于File是 Dagger 的一等 DAG 对象,http的结果可以直接参与后续操作,例如放进容器:

query { container { from(address: "alpine:latest") { withFile( path: "/etc/motd" file: {http(url: "http://dagger.io") { id }} ) { stdout: { # ... 后续构建步骤 } } } } }

(Go SDK 对应写法为client.Container().From("alpine:latest").WithFile("/etc/motd", client.HTTP(url)),与 TestHTTP 中c.HTTP(url).Contents(ctx)的调用形态一致。)

底层机制二:HTTPState——按 URL 的持久化缓存与增量校验

这是当前仓库实现中http最值得展开的部分:普通 URL 拉取并不每次都真的下载,而是走一个内部持久对象HTTPState

从 schema 安装代码 可以看到,http解析器实际是两段选择器串联:

  1. Query._httpState(url)—— 返回一个只由 URL 决定的HTTPState对象(类型定义:URL+ETag+LastModified+ContentDigest+ 快照引用),它实现了dagql.PersistedObject,可以在会话间持久化、按需恢复;
  2. HTTPState._resolve(checksum, permissions, name)—— 真正执行条件请求并产出File

HTTPState.Resolve(核心实现)的关键逻辑:

  • 条件请求:如果状态里有ETag,发送If-None-Match;否则若有LastModified,发送If-Modified-Since(请求头构造);
  • 304 命中:服务端返回HTTP 304 Not Modified时不产生新快照,直接复用已缓存的快照,更新 ETag/Last-Modified 并做 checksum 比对后返回(304 分支)。源码注释特别提到:304 重校验不产生任何下载进度输出;
  • 200 更新:下载新内容,通过MountRef把响应体写入快照中的contents文件(写快照),同时计算 sha256 摘要并记录 ETag/Last-Modified;若内容摘要与缓存一致则丢弃新快照,不一致则替换旧快照(快照替换);
  • 会话级隔离WithInput(dagql.PerSessionInput)意味着缓存按会话划分。TestHTTPCachePerSessions 用一个每次请求返回不同计数值的服务验证:不同会话各自得到自己的缓存副本,互不串扰。

持久化方面,HTTPState编码时只序列化url/etag/lastModified/contentDigest(persistedHTTPStatePayload),快照以snapshot角色的快照链接挂接,解码时按需重新打开快照(DecodePersistedObject)。这使得跨进程/跨重启恢复会话时,能继续利用 ETag 做增量校验而不是重新全量下载。

另外,最终File结果的内容摘要在 newHTTPFileResult 中由文件路径 + 权限 + 内容摘要 + LastModified + checksum 参数共同哈希得出——也就是说,改权限或改期望摘要都会得到一个不同的 Dagger 对象,从而正确参与构建缓存的判定。

访问引擎内服务:experimentalServiceHost

experimentalServiceHost参数解决的是"URL 指向 Dagger 引擎内部服务网络中的服务"这一场景(例如http://<service-hostname>:8080/...)。解析流程(resolveHTTPSessionContext):

  1. 加载ServiceID,取得其内容摘要与Hostname
  2. 构造ServiceBinding{Service, Hostname}并调用query.Services().StartBindings(...),把该服务启动并绑定到会话,返回一个detach函数;
  3. 完成拉取后defer detach()释放绑定。

测试侧的覆盖相当完整(core/integration/http_test.go):

  • TestHTTPService:起一个服务并以其 hostname 构造 URL,http查询成功取回内容;
  • TestHTTPChecksum / TestHTTPChecksumMismatch / TestHTTPChecksumInvalid:checksum 匹配、不匹配(http checksum mismatch)、格式非法(invalid checksum)三种路径;
  • TestHTTPAuth:结合authHeader(Secret)完成 Basic Auth 拉取;
  • TestHTTPTimestamp:验证Last-Modified落到文件 mtime。

小结:把 http 查询放进你的流水线

把上面的内容落到实践中,可以掌握这些能力:

  • 最小用法http(url: "...") { size contents }即可把任意公开 URL 变成可查询的文件对象(标准示例);
  • 可控命名与权限name决定文件在后续withFile等步骤中的名字,permissions控制落盘权限(默认0600);
  • 完整性保障checksum: "sha256:..."在摘要不符时让构建快速失败,适合下载固定版本的可执行文件或补丁文件;
  • 私有资源authHeader以 Secret 形式注入Authorization头,拉取私有仓库、制品库资源;
  • 服务内网资源experimentalServiceHost先启动指定服务再拉取其 hostname 上的 URL;
  • 缓存语义:同一 URL 的拉取被缓存为可持久化的HTTPState,后续会话通过 ETag/Last-Modified做 304 增量校验,缓存按会话隔离——这保证了重复构建时的下载开销最小化。

以上所有行为均可在仓库中对应验证:schema 与参数定义见 core/schema/http.go,核心拉取/缓存/持久化实现见 core/http.go,单元与集成测试分别见 core/schema/http_test.go 和 core/integration/http_test.go。

【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询