news 2026/9/26 15:49:00

OpenShell Go SDK 完整开发指南:面向 Kubernetes 生态的云沙箱客户端

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenShell Go SDK 完整开发指南:面向 Kubernetes 生态的云沙箱客户端

【免费下载链接】OpenShell

OpenShell is the safe, private runtime for autonomous AI agents.

项目地址:https://gitcode.com/gh_mirrors/op/OpenShell
点击查看免费下载

本指南以sdk/go/README.md为骨架,结合仓库源码,系统讲解 OpenShell Go SDK 的设计动机、客户端架构、认证体系、沙箱生命周期管理与命令执行等核心能力。读完本文,你将掌握如何用与 Kubernetes client-go 同构的 Go 客户端连接 OpenShell 网关、创建并等待沙箱就绪、执行/流式/交互式命令、处理分页与令牌自动刷新,以及如何借助内置 Fake 客户端进行无网关测试。

为什么需要 Go SDK

OpenShell 是面向自治 AI Agent 的安全、私有运行时。如果你要构建 Operator、Controller 或任何将 OpenShell 资源当作原生 Kubernetes 对象来管理的自动化程序,就需要一个 Go 客户端——Go 正是 Kubernetes 生态的语言。

因此,这个 SDK 刻意对标k8s.io/client-go(每个 Kubernetes Operator 开发者都熟悉的标准客户端库),模式高度一致(见 client.go 中的ClientInterface定义):

  • 按资源划分的类型化子客户端:client.Sandboxes()、client.Providers()、client.Exec(),就像clientset.CoreV1().Pods()一样;
  • 领域类型与线上格式分离:干净的 Go 结构体集中在types包,公共 API 不泄露 proto 细节(类似k8s.io/api),源码中的类型别名集中定义在 types_reexport.go;
  • Watch 原语:基于 channel 的 Watcher,提供ResultChan()与Stop(),与 client-go 的watch.Interface行为一致;
  • 函数式选项:列表过滤、分页、watch 配置都通过变参 option 传递;每个入口点对 nil option 静默忽略,所以条件性构造的 option 切片可以原样传入,无需预先过滤 nil;
  • 可组合的带令牌刷新的认证:封装oauth2.TokenSource实现令牌自动缓存与合并刷新(合并并发刷新调用),对应 client-go 的cachingTokenSource模式;
  • Fake 客户端:完整客户端接口的内存实现(对标k8s.io/client-go/kubernetes/fake),无需真实网关即可测试 Operator。

架构总览:Client 子客户端树

从 client.go 的接口定义可以看到 SDK 顶层形态。NewClient创建 gRPC 连接后,把全部子客户端一次性接线完毕(client.go):

Client ├── Sandboxes() → SandboxInterface (create, get, list, delete, watch, wait, logs) ├── SandboxTemplates() → SandboxTemplateInterface (可复用工作负载模板) ├── Exec() → ExecInterface (run, stream, interactive) ├── Files() → FileInterface (upload, download) ├── Health() → HealthInterface (health check, gateway info, current user) ├── Services() → ServiceInterface (expose, get, list, delete) ├── Providers() → ProviderInterface (CRUD + ensure) │ ├── Profiles() → ProfileInterface (list, get, import, update, lint, delete) │ └── Refresh() → RefreshInterface (configure, status, rotate, delete) ├── Workspaces() → WorkspaceInterface (create, get, list, delete, members) ├── Config() → ConfigInterface (网关/沙箱配置读写) ├── Policy() → PolicyInterface (draft review, approve, reject, merge, status) ├── SSH() → SSHInterface (SSH 会话) └── TCP() → TCPInterface (TCP 端口转发)

所有领域类型都位于openshell/v1/types/包,proto 到 Go 的转换发生在内部 converter 层(sdk/go/openshell/v1/internal/converter/),公共 API 通过类型别名保持单包导入。Close()是幂等的(内部用sync.Once保护),可安全多次调用(client.go)。

快速开始:连接、建沙箱、跑命令

最小可运行示例(也是 README.md 的核心示例)如下:

import v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" // Connect to a gateway client, err := v1.NewClient(v1.Config{ Address: "gateway.example.com:443", Auth: v1.StaticToken("my-token"), }) if err != nil { log.Fatal(err) } defer client.Close() // Create a sandbox and wait until it's ready sandbox, err := client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{ Template: &v1.SandboxTemplate{Image: "python:3.12"}, }, nil) if err != nil { log.Fatal(err) } sandbox, err = client.Sandboxes().WaitReady(ctx, "default", sandbox.Name) if err != nil { log.Fatal(err) } // Run a command result, err := client.Exec().Run(ctx, "default", sandbox.Name, []string{"python3", "-c", "print('hello from sandbox')"}, v1.ExecOptions{}, ) if err != nil { log.Fatal(err) } fmt.Println(string(result.Stdout))

几个值得注意的底层行为:

  • NewClient要求cfg.Address非空,否则返回ErrorInvalidArgument类型的StatusError;cfg.Auth为 nil 时自动降级为NoAuth()(client.go)。Config还支持TLS(CertFile/KeyFile/CAFile/Insecure)、Timeout、RetryPolicy、Logger等字段。
  • SandboxInterface的完整能力见 sandbox.go:除Create/Get/Delete外,还有Start/Stop、WaitReady/WaitStopped、Watch、GetLogs,以及AttachProvider/DetachProvider和ListProviders等 provider 挂载操作。
  • ExecInterface提供三种执行模式(exec.go):Run(收集型,返回完整ExecResult)、Stream(逐块输出迭代器)、Interactive(双向 I/O 的 PTY 会话)。

分页:惰性 Pager

List 方法返回一个惰性分页器,不会立即发出请求。NextPage用传入的 context 拉取一页,ListAll显式耗尽所有页。PageSize是每页请求上限,PageToken用于从之前的查询位置续读:

pager, err := client.Sandboxes().List("default", v1.ListOptions{PageSize: 100}) if err != nil { log.Fatal(err) } for { page, err := pager.NextPage(ctx) if err != nil { log.Fatal(err) } if page == nil { break } for _, sandbox := range page.Items { fmt.Println(sandbox.Name) } }

认证体系:从静态令牌到 OIDC 与边缘代理

SDK 的认证通过实现 gRPCcredentials.PerRPCCredentials接口的AuthProvider抽象,可任意组合。基础实现见 auth.go:StaticToken发送Authorization: Bearer <token>,NoAuth不发送任何凭证。

自动令牌刷新:RefreshableToken

针对 OIDC 网关,RefreshableToken包装任意oauth2.TokenSource,提供自动缓存与合并刷新。其实现位于 auth_refresh.go:

import "golang.org/x/oauth2" tokenSource := oauth2Config.TokenSource(ctx, initialToken) auth, err := v1.RefreshableToken(tokenSource, v1.WithLeeway(30*time.Second), ) if err != nil { log.Fatal(err) } client, err := v1.NewClient(v1.Config{ Address: "gateway.example.com:443", Auth: auth, }) if err != nil { log.Fatal(err) } defer client.Close()

关键实现细节(全部可在源码中验证):

  • Leeway:WithLeeway设置在令牌到期前多少时间触发主动刷新,默认 10 秒(defaultLeeway = 10 * time.Second);负值会被截为 0。
  • 合并刷新:并发调用者共享同一次刷新,通过singleflight.Group的DoChan实现(auth_refresh.go),避免令牌端点雪崩。
  • 失败降级与退避:令牌源刷新失败时,SDK 会回退到已缓存令牌并记录警告日志(需用WithLogger配置 logger,否则警告静默丢弃);同时进入指数退避(1s、2s、4s……上限 30s),避免放大令牌端点故障。退避期间若无缓存令牌,会直接返回错误。
  • 日志接口:WithLogger(l types.Logger)可注入结构化 logger,用于输出刷新失败警告与隧道事件。

边缘代理:extra headers、Cloudflare Access 与 WebSocket 隧道

当网关位于零信任反向代理之后时,用WithExtraHeaders在标准认证之外附加代理专用请求头:

base := v1.StaticToken("my-gateway-token") auth, err := v1.WithExtraHeaders(base, map[string]string{ "x-proxy-auth": "proxy-secret", }) if err != nil { log.Fatal(err) } client, err := v1.NewClient(v1.Config{ Address: "gateway.example.com:443", Auth: auth, })

对于 Cloudflare Access,edge包提供便捷构造器(cloudflare.go),它会设置cf-access-jwt-assertion头与CF_Authorizationcookie:

import "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/edge" auth, err := edge.CloudflareAccess(base, os.Getenv("CF_ACCESS_TOKEN"))

对于拒绝 HTTP/2 的边缘代理,可以使用 WebSocket 隧道。edge.NewTunnelProxy(tunnel.go)在127.0.0.1上监听一个临时端口,每个被接受的连接都通过携带边缘令牌的 WebSocket 握手拨号到网关,并双向拷贝数据。gRPC 客户端改拨tunnel.Addr()即可:

tunnel, err := edge.NewTunnelProxy( "wss://gateway.example.com/ws", os.Getenv("CF_ACCESS_TOKEN"), ) if err != nil { log.Fatal(err) } defer tunnel.Close() client, err := v1.NewClient(v1.Config{ Address: tunnel.Addr(), Auth: v1.StaticToken("my-token"), TLS: &v1.TLSConfig{Insecure: true}, // local tunnel, no TLS })

隧道实现细节:ws://仅允许 loopback 主机,携带边缘令牌的远程连接必须使用wss://;每条桥接连接设置 64 MiB 的读取上限以容纳 gRPC 帧;Close()默认等待在途连接最多 5 秒排空(WithCloseTimeout可调),超时后强制关闭。

OIDC 登录与可再生服务认证

oidc包提供网关感知的 OIDC 认证,涵盖浏览器、键盘、设备码和客户端凭证四种流程:

import "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/oidc" // Gateway-aware login: reads OIDC config from gateway metadata token, err := oidc.Login(ctx, "my-gateway") if err != nil { log.Fatal(err) } // Use the token with the SDK client client, err := v1.NewClient(v1.Config{ Address: "gateway.example.com:443", Auth: v1.StaticToken(token.AccessToken), })

无头环境使用设备码流程:

token, err := oidc.DeviceLogin(ctx, oidc.WithIssuer("https://auth.example.com"), oidc.WithClientID("my-app"), )

一次性服务账号令牌交换用ClientCredentials;长驻 SDK 客户端则挂载可再生、仅内存的认证提供者:

auth, err := oidc.NewClientCredentialsAuth( oidc.WithGateway("my-gateway"), oidc.WithClientSecretProvider(func(context.Context) (string, error) { return os.Getenv("OPENSHELL_OIDC_CLIENT_SECRET"), nil }), ) if err != nil { log.Fatal(err) } client, err := v1.NewClient(v1.Config{ Address: "gateway.example.com:443", Auth: auth, TLS: tlsConfig, }) if err != nil { log.Fatal(err) } defer client.Close()

除WithGateway(从网关元数据自动读取 OIDC 配置)外,也可显式指定WithIssuer、WithClientID、WithScopes、WithAudience。该提供者会在凭证到期前重复授权,且绝不把 client secret 或 access token 写入磁盘。完整选项与流程见 oidc 包源码。

从 CLI 配置自动接线:gateway 包

gateway包能直接加载 CLI 侧已保存的网关配置并自动装配认证(gateway.go)。gateway.NewClient(name)解析网关目录、读取metadata.json、惰性加载令牌,并按认证模式映射到对应的 SDKAuthProvider:AuthModeNone/AuthModePlaintext映射到NoAuth(),AuthModeCloudflareJWT映射到惰性边缘认证(令牌文件缺失时NewClient依然成功,错误在首次认证时才暴露),AuthModeOIDC映射到RefreshableToken(磁盘令牌源)。name为空时使用活动网关(由openshell gateway use设置);LoadConfig则只解析配置不建连。ListGateways枚举用户与系统目录下所有网关,同名时用户网关优先。调用方也可用WithAuth()/WithTLS()等ClientOption覆盖默认行为。

交互式会话控制与退出码语义

SDK 会话实现可选的InteractiveSessionControl接口(exec.go),在原有InteractiveSession(Read/Write/Resize/ExitCode/Close)之外新增CloseWrite()(关闭 stdin、保留输出)与Cancel()(中止 RPC):

  • CloseInteractiveInput(session):对支持CloseWrite的会话关闭输入;对不支持(如自定义 mock)的会话返回ErrorUnimplemented并保持会话打开。
  • CancelInteractive(session):优先调用Cancel(),否则回退到Close()。
  • SDK 的关闭/取消操作是幂等的;输入关闭后的写入与 resize 返回io.ErrClosedPipe。
  • ExitCode()会等待最终 gRPC 状态,返回观察到的进程退出码及随后的流错误——退出事件本身并不代表流成功结束,需要并发地Read排空输出并同时等待ExitCode()。

功能一览

功能接口源码入口
沙箱生命周期(create/get/list/delete/watch/wait/logs)SandboxInterfacesandbox.go
命令执行(收集、流式、交互式 PTY)ExecInterfaceexec.go
Provider 管理(CRUD + 幂等 ensure)ProviderInterfaceprovider.go
Provider 配置(list/import/lint/update)ProfileInterfaceprofile.go
凭证刷新(configure/rotate/status)RefreshInterfacerefresh.go
服务暴露(expose/list/delete)ServiceInterfaceservice.go
文件传输(受传输能力门控)FileInterfacefile.go
策略管理(draft review/approve/reject/merge/global)PolicyInterfacepolicy.go
沙箱日志(流式获取,支持行数/时间/来源/级别过滤)SandboxInterfacesandbox.go
工作区管理(create/get/list/delete/members)WorkspaceInterfaceworkspace.go
沙箱 provider 挂载(attach/detach/list)SandboxInterfacesandbox.go
网关信息与当前用户身份HealthInterfacehealth.go
SSH 隧道与 TCP 转发SSHInterface,TCPInterfacessh.go, tcp.go
认证:静态令牌、可刷新令牌(oauth2.TokenSource)AuthProviderauth.go, auth_refresh.go
边缘认证:extra headers、Cloudflare Access、WebSocket 隧道AuthProvider,edge.TunnelProxycloudflare.go, tunnel.go
类型化错误(IsNotFound/IsAlreadyExists/IsConflict…)StatusErrorerrors.go
实时 watch(终态自动停止)WatchInterface[T]watch.go
无 gRPC 服务器的 Fake 客户端fake.Clientfake.go
OIDC 登录与可再生服务认证oidc.Login/oidc.DeviceLogin/oidc.ClientCredentials/oidc.NewClientCredentialsAuthoidc 包
网关配置便捷加载gateway.NewClient/gateway.LoadConfiggateway.go

用 Fake 客户端做无网关测试

fake.Client(fake.go)完整实现v1.ClientInterface,所有子客户端基于内存 store。它特别适合在 CI 中测试 Operator 逻辑:NewClient(opts...)直接构造(无网络、无 gRPC),AddSandbox/AddProvider/AddWorkspace/AddMember/AddRevision等Add*方法用于在测试开始前预置 fixture(插入时深拷贝、不触发 watch 事件),Close()停止所有 watcher 并让后续调用返回 Unavailable。也可用WithHealthResult/WithGatewayInfo/WithCurrentUser定制健康检查返回值。编译期通过var _ v1.ClientInterface = (*Client)(nil)保证接口完整。

从 v0.0.101 迁移

预 1.0 版本 SDK 刻意包含一批源码不兼容的 API 修正(README 明确声明),迁移时注意:

  • TCP.Listen返回ForwardListener生命周期句柄,SDK 拥有 accept 循环,调用方拨Addr()并Close(),而不是自己调Accept()或把句柄交给http.Serve;
  • 资源操作改为显式传工作区,携带工作区的领域类型保留该作用域;
  • 若干公共结构体字段顺序变化,请使用键控结构体字面量(keyed struct literals);
  • 首字母缩写采用 Go 拼写,包括JSONRPCMaxBodyBytes;
  • Provider 配置时长使用精确的RefreshBefore、MaxLifetime、CacheTTL字段,旧的整秒字段已移除。

这些是模块停留在 v1 之下的有意变更,建议作为一次整体迁移升级,而不是继续依赖 v0.0.101 的 API 形态。

前置条件与构建测试

SDK 要求Go 1.25.13 或更高版本,并推荐使用 mise)。在克隆本仓库后进入 SDK 目录即可:

cd sdk/go mise run test # Run tests with coverage mise run lint # Run golangci-lint mise run ci # Full CI pipeline (lint + build + test)

许可证为 Apache-2.0,详见仓库根目录 LICENSE;Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

【免费下载链接】OpenShell

OpenShell is the safe, private runtime for autonomous AI agents.

项目地址:https://gitcode.com/gh_mirrors/op/OpenShell
点击查看免费下载
上一篇:bspwm源码单元测试:test_window.c解析与扩展方法
下一篇:Python高级编程:使用Jinja2模板引擎打造智能代码生成器的完整指南

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 15:47:01

飞书MCP协议详解:大模型与应用的标准化通信接口

1. 飞书MCP到底是什么——不是新功能&#xff0c;而是协议层的“水电煤”飞书官方MCP&#xff08;Model Communication Protocol&#xff09;上线这件事&#xff0c;最近在开发者圈子里传得挺快&#xff0c;但很多人点开文档第一眼就懵了&#xff1a;这玩意儿既不像飞书机器人那…

作者头像 李华
网站建设 2026/9/26 15:46:59

Inpaint-web:免安装的浏览器图片修复与超分

Inpaint-web&#xff1a;免安装的浏览器图片修复与超分 【免费下载链接】inpaint-web A free and open-source inpainting & image-upscaling tool powered by webgpu and wasm on the browser。| 基于 Webgpu 技术和 wasm 技术的免费开源 inpainting & image-upscalin…

作者头像 李华
网站建设 2026/9/26 15:46:51

MCP-A2A-Agent Skills-ACP 实战:用 TaoToken 统一 Key 打通多 Agent 协作配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 15:45:50

ArcGIS Engine地图整饰:C#动态控制指北针、比例尺与图例

简介&#xff1a;本资源是一套基于C#与ArcGIS Engine开发的地图整饰与输出实战项目&#xff0c;面向GIS开发初学者及中级程序员&#xff0c;解决地图制图中指北针、图例、比例尺、格网等核心整饰要素的代码实现与工程集成问题。包内共140个文件&#xff0c;涵盖13个关键C#源码文…

作者头像 李华