From 8cd6c7b69ca2475de1c9873cee7ec897407d1ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Hu=C3=9F?= Date: Wed, 4 Nov 2026 15:41:23 +0100 Subject: [PATCH 1/2] chore(sdk/go)!: remove the Go SDK, now at github.com/ai-openshell/sdk-go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Go SDK moved to github.com/ai-openshell/sdk-go (#5031), and the final sdk/go tag carries the deprecation notice. Remove what is left of it from this repository: - sdk/go/, tasks/go.toml, and the Go toolchain and tools in mise.toml and mise.lock (nothing else here is written in Go) - the Go entry, setup, and build steps in CodeQL, and sdk/go in the CodeQL config - sdk-go-tag.yml and the tag-sdk-go job in release-publish.yml, including its needs entry and the expected-publisher check - the Go SDK Freeze job in Branch Checks, which has nothing left to guard docs/sdk/go.mdx now shows the new module path, pins the SDK version to the gateway's, and keeps the migration steps from the old module that were reviewed on #5245. README, CONTRIBUTING, AGENTS, TESTING, CI.md, buf.yaml, and the Windows skill drop or redirect their sdk/go references. Existing sdk/go/vX.Y.Z tags stay, so builds that use the old module path keep working. BREAKING CHANGE: github.com/NVIDIA/OpenShell/sdk/go is no longer developed in this repository; use github.com/ai-openshell/sdk-go. Signed-off-by: Roland Huß --- .../build-openshell-mxc-windows/SKILL.md | 7 +- .github/codeql/codeql-config.yml | 1 - .github/workflows/branch-checks.yml | 33 - .github/workflows/codeql.yml | 14 - .github/workflows/release-publish.yml | 14 +- .github/workflows/sdk-go-tag.yml | 58 - AGENTS.md | 5 +- CI.md | 1 - CONTRIBUTING.md | 1 - README.md | 2 +- TESTING.md | 3 +- buf.yaml | 6 +- docs/sdk/go.mdx | 33 +- mise.lock | 40 - mise.toml | 5 - sdk/go/.golangci.yml | 43 - sdk/go/README.md | 18 - sdk/go/buf.gen.yaml | 32 - sdk/go/docs/book.toml | 23 - sdk/go/docs/src/SUMMARY.md | 38 - sdk/go/docs/src/api/client.md | 79 - sdk/go/docs/src/api/config.md | 52 - sdk/go/docs/src/api/edge.md | 91 - sdk/go/docs/src/api/exec.md | 175 - sdk/go/docs/src/api/fake.md | 122 - sdk/go/docs/src/api/files.md | 36 - sdk/go/docs/src/api/gateway.md | 188 - sdk/go/docs/src/api/health.md | 32 - sdk/go/docs/src/api/oidc.md | 173 - sdk/go/docs/src/api/overview.md | 65 - sdk/go/docs/src/api/policy.md | 128 - sdk/go/docs/src/api/profiles.md | 86 - sdk/go/docs/src/api/providers.md | 161 - sdk/go/docs/src/api/refresh.md | 57 - sdk/go/docs/src/api/sandbox-templates.md | 127 - sdk/go/docs/src/api/sandboxes.md | 304 - sdk/go/docs/src/api/services.md | 53 - sdk/go/docs/src/api/ssh.md | 55 - sdk/go/docs/src/api/tcp.md | 60 - sdk/go/docs/src/architecture.md | 139 - sdk/go/docs/src/error-handling.md | 202 - sdk/go/docs/src/getting-started.md | 123 - sdk/go/docs/src/introduction.md | 34 - sdk/go/docs/src/testing.md | 184 - sdk/go/docs/theme/custom.css | 184 - sdk/go/go.mod | 23 - sdk/go/go.sum | 54 - sdk/go/mise.toml | 154 - sdk/go/openshell/v1/auth.go | 48 - sdk/go/openshell/v1/auth_extra.go | 80 - sdk/go/openshell/v1/auth_extra_test.go | 177 - sdk/go/openshell/v1/auth_refresh.go | 183 - sdk/go/openshell/v1/auth_refresh_test.go | 571 - sdk/go/openshell/v1/auth_test.go | 36 - sdk/go/openshell/v1/client.go | 171 - sdk/go/openshell/v1/client_test.go | 67 - sdk/go/openshell/v1/config.go | 67 - sdk/go/openshell/v1/config_client.go | 68 - sdk/go/openshell/v1/config_client_test.go | 645 - sdk/go/openshell/v1/context_errors.go | 20 - sdk/go/openshell/v1/context_errors_test.go | 49 - sdk/go/openshell/v1/doc.go | 466 - sdk/go/openshell/v1/edge/cloudflare.go | 29 - sdk/go/openshell/v1/edge/cloudflare_test.go | 88 - sdk/go/openshell/v1/edge/doc.go | 88 - sdk/go/openshell/v1/edge/options_nil_test.go | 23 - sdk/go/openshell/v1/edge/tunnel.go | 294 - sdk/go/openshell/v1/edge/tunnel_test.go | 500 - sdk/go/openshell/v1/errors.go | 66 - sdk/go/openshell/v1/errors_test.go | 127 - sdk/go/openshell/v1/example_fake_test.go | 155 - sdk/go/openshell/v1/example_test.go | 184 - sdk/go/openshell/v1/exec.go | 70 - sdk/go/openshell/v1/exec_client.go | 341 - sdk/go/openshell/v1/exec_client_test.go | 764 - sdk/go/openshell/v1/exec_control_test.go | 56 - sdk/go/openshell/v1/fake/broadcaster.go | 155 - sdk/go/openshell/v1/fake/broadcaster_test.go | 195 - sdk/go/openshell/v1/fake/config.go | 56 - sdk/go/openshell/v1/fake/config_test.go | 78 - sdk/go/openshell/v1/fake/doc.go | 37 - sdk/go/openshell/v1/fake/exec.go | 57 - sdk/go/openshell/v1/fake/exec_test.go | 70 - sdk/go/openshell/v1/fake/fake.go | 240 - sdk/go/openshell/v1/fake/fake_test.go | 298 - sdk/go/openshell/v1/fake/file.go | 52 - sdk/go/openshell/v1/fake/file_test.go | 52 - sdk/go/openshell/v1/fake/health.go | 111 - sdk/go/openshell/v1/fake/health_test.go | 161 - sdk/go/openshell/v1/fake/mutations.go | 19 - sdk/go/openshell/v1/fake/options_nil_test.go | 30 - sdk/go/openshell/v1/fake/pager.go | 48 - sdk/go/openshell/v1/fake/policy.go | 240 - sdk/go/openshell/v1/fake/policy_test.go | 287 - sdk/go/openshell/v1/fake/profile.go | 81 - sdk/go/openshell/v1/fake/profile_test.go | 100 - sdk/go/openshell/v1/fake/provider.go | 190 - sdk/go/openshell/v1/fake/provider_test.go | 276 - sdk/go/openshell/v1/fake/refresh.go | 57 - sdk/go/openshell/v1/fake/refresh_test.go | 78 - sdk/go/openshell/v1/fake/sandbox.go | 932 - sdk/go/openshell/v1/fake/sandbox_template.go | 292 - .../v1/fake/sandbox_template_test.go | 468 - sdk/go/openshell/v1/fake/sandbox_test.go | 1039 - sdk/go/openshell/v1/fake/service.go | 65 - sdk/go/openshell/v1/fake/service_test.go | 72 - sdk/go/openshell/v1/fake/ssh.go | 58 - sdk/go/openshell/v1/fake/ssh_test.go | 87 - sdk/go/openshell/v1/fake/store.go | 174 - sdk/go/openshell/v1/fake/store_test.go | 293 - sdk/go/openshell/v1/fake/tcp.go | 61 - sdk/go/openshell/v1/fake/tcp_test.go | 104 - sdk/go/openshell/v1/fake/workspace.go | 196 - sdk/go/openshell/v1/fake/workspace_test.go | 294 - sdk/go/openshell/v1/file.go | 20 - sdk/go/openshell/v1/file_client.go | 123 - sdk/go/openshell/v1/file_client_test.go | 343 - sdk/go/openshell/v1/gateway/config.go | 167 - sdk/go/openshell/v1/gateway/config_test.go | 213 - sdk/go/openshell/v1/gateway/doc.go | 75 - sdk/go/openshell/v1/gateway/errors.go | 36 - sdk/go/openshell/v1/gateway/errors_test.go | 88 - sdk/go/openshell/v1/gateway/gateway.go | 182 - sdk/go/openshell/v1/gateway/gateway_test.go | 534 - sdk/go/openshell/v1/gateway/options.go | 63 - .../openshell/v1/gateway/options_nil_test.go | 23 - sdk/go/openshell/v1/gateway/paths.go | 199 - sdk/go/openshell/v1/gateway/paths_test.go | 374 - sdk/go/openshell/v1/gateway/token.go | 167 - sdk/go/openshell/v1/gateway/token_test.go | 271 - sdk/go/openshell/v1/health.go | 55 - sdk/go/openshell/v1/health_client.go | 48 - sdk/go/openshell/v1/health_client_test.go | 271 - sdk/go/openshell/v1/integration_test.go | 83 - .../openshell/v1/internal/converter/copy.go | 68 - .../v1/internal/converter/coverage_test.go | 464 - .../openshell/v1/internal/converter/errors.go | 84 - .../v1/internal/converter/errors_test.go | 163 - .../openshell/v1/internal/converter/exec.go | 99 - .../v1/internal/converter/exec_test.go | 194 - .../openshell/v1/internal/converter/health.go | 103 - .../v1/internal/converter/health_test.go | 185 - sdk/go/openshell/v1/internal/converter/log.go | 47 - .../v1/internal/converter/log_test.go | 97 - .../v1/internal/converter/network_policy.go | 335 - .../internal/converter/network_policy_test.go | 352 - .../openshell/v1/internal/converter/policy.go | 391 - .../v1/internal/converter/policy_test.go | 709 - .../v1/internal/converter/profile.go | 500 - .../v1/internal/converter/profile_test.go | 753 - .../v1/internal/converter/provider.go | 104 - .../v1/internal/converter/provider_test.go | 233 - .../v1/internal/converter/refresh.go | 117 - .../v1/internal/converter/refresh_test.go | 212 - .../v1/internal/converter/sandbox.go | 618 - .../v1/internal/converter/sandbox_test.go | 818 - .../v1/internal/converter/service.go | 75 - .../v1/internal/converter/service_test.go | 137 - .../v1/internal/converter/setting.go | 334 - .../v1/internal/converter/setting_test.go | 936 - sdk/go/openshell/v1/internal/converter/ssh.go | 42 - .../v1/internal/converter/ssh_test.go | 113 - .../openshell/v1/internal/converter/time.go | 122 - .../v1/internal/converter/time_test.go | 91 - .../v1/internal/converter/workspace.go | 97 - .../v1/internal/converter/workspace_test.go | 209 - sdk/go/openshell/v1/internal/grpc/conn.go | 99 - .../openshell/v1/internal/grpc/conn_test.go | 100 - .../v1/internal/oidctoken/oidctoken.go | 53 - .../openshell/v1/internal/options/options.go | 19 - .../v1/internal/options/options_test.go | 98 - sdk/go/openshell/v1/logger.go | 12 - sdk/go/openshell/v1/mutations.go | 27 - sdk/go/openshell/v1/oidc/authcode.go | 236 - sdk/go/openshell/v1/oidc/authcode_test.go | 371 - sdk/go/openshell/v1/oidc/browser.go | 59 - sdk/go/openshell/v1/oidc/browser_test.go | 54 - sdk/go/openshell/v1/oidc/credentials.go | 186 - sdk/go/openshell/v1/oidc/credentials_auth.go | 90 - sdk/go/openshell/v1/oidc/credentials_test.go | 662 - sdk/go/openshell/v1/oidc/device.go | 291 - sdk/go/openshell/v1/oidc/device_test.go | 698 - sdk/go/openshell/v1/oidc/discovery.go | 182 - sdk/go/openshell/v1/oidc/discovery_test.go | 263 - sdk/go/openshell/v1/oidc/doc.go | 80 - sdk/go/openshell/v1/oidc/errors.go | 43 - sdk/go/openshell/v1/oidc/errors_test.go | 107 - sdk/go/openshell/v1/oidc/example_test.go | 160 - sdk/go/openshell/v1/oidc/keyboard.go | 97 - sdk/go/openshell/v1/oidc/keyboard_test.go | 172 - sdk/go/openshell/v1/oidc/oidc.go | 233 - sdk/go/openshell/v1/oidc/oidc_test.go | 547 - sdk/go/openshell/v1/oidc/options.go | 189 - sdk/go/openshell/v1/oidc/options_nil_test.go | 23 - sdk/go/openshell/v1/oidc/options_test.go | 155 - sdk/go/openshell/v1/oidc/token.go | 75 - sdk/go/openshell/v1/oidc/token_test.go | 123 - sdk/go/openshell/v1/options.go | 50 - sdk/go/openshell/v1/options_nil_test.go | 54 - sdk/go/openshell/v1/pager.go | 112 - sdk/go/openshell/v1/pager_test.go | 121 - sdk/go/openshell/v1/policy.go | 118 - sdk/go/openshell/v1/policy_client.go | 192 - sdk/go/openshell/v1/policy_client_test.go | 1033 - sdk/go/openshell/v1/profile.go | 65 - sdk/go/openshell/v1/profile_client.go | 195 - sdk/go/openshell/v1/profile_client_test.go | 615 - sdk/go/openshell/v1/provider.go | 30 - sdk/go/openshell/v1/provider_client.go | 147 - sdk/go/openshell/v1/provider_client_test.go | 356 - sdk/go/openshell/v1/refresh.go | 48 - sdk/go/openshell/v1/refresh_client.go | 72 - sdk/go/openshell/v1/refresh_client_test.go | 430 - sdk/go/openshell/v1/routing.go | 95 - sdk/go/openshell/v1/routing_test.go | 192 - sdk/go/openshell/v1/sandbox.go | 124 - sdk/go/openshell/v1/sandbox_client.go | 493 - sdk/go/openshell/v1/sandbox_client_test.go | 1544 -- sdk/go/openshell/v1/sandbox_template.go | 46 - .../openshell/v1/sandbox_template_client.go | 104 - .../v1/sandbox_template_client_test.go | 270 - sdk/go/openshell/v1/service.go | 40 - sdk/go/openshell/v1/service_client.go | 104 - sdk/go/openshell/v1/service_client_test.go | 355 - sdk/go/openshell/v1/ssh.go | 37 - sdk/go/openshell/v1/ssh_client.go | 157 - sdk/go/openshell/v1/ssh_client_test.go | 635 - sdk/go/openshell/v1/tcp.go | 76 - sdk/go/openshell/v1/tcp_client.go | 349 - sdk/go/openshell/v1/tcp_client_test.go | 1306 - sdk/go/openshell/v1/types.go | 59 - sdk/go/openshell/v1/types/auth.go | 13 - sdk/go/openshell/v1/types/config.go | 16 - sdk/go/openshell/v1/types/doc.go | 10 - sdk/go/openshell/v1/types/errors.go | 156 - sdk/go/openshell/v1/types/exec.go | 17 - sdk/go/openshell/v1/types/health.go | 69 - sdk/go/openshell/v1/types/log.go | 100 - sdk/go/openshell/v1/types/logger.go | 12 - sdk/go/openshell/v1/types/mutations.go | 28 - sdk/go/openshell/v1/types/network_policy.go | 244 - sdk/go/openshell/v1/types/options.go | 107 - sdk/go/openshell/v1/types/options_nil_test.go | 29 - sdk/go/openshell/v1/types/options_test.go | 40 - sdk/go/openshell/v1/types/policy.go | 417 - sdk/go/openshell/v1/types/profile.go | 192 - sdk/go/openshell/v1/types/provider.go | 38 - sdk/go/openshell/v1/types/refresh.go | 84 - sdk/go/openshell/v1/types/sandbox.go | 278 - sdk/go/openshell/v1/types/service.go | 34 - sdk/go/openshell/v1/types/setting.go | 122 - sdk/go/openshell/v1/types/ssh.go | 34 - sdk/go/openshell/v1/types/types.go | 69 - sdk/go/openshell/v1/types/watch.go | 18 - sdk/go/openshell/v1/types/workspace.go | 51 - sdk/go/openshell/v1/types_reexport.go | 96 - sdk/go/openshell/v1/watch.go | 46 - sdk/go/openshell/v1/watch_test.go | 137 - sdk/go/openshell/v1/workspace.go | 49 - sdk/go/openshell/v1/workspace_client.go | 174 - sdk/go/openshell/v1/workspace_scope.go | 25 - sdk/go/openshell/v1/workspace_test.go | 495 - sdk/go/proto/datamodelv1/datamodel.pb.go | 746 - sdk/go/proto/openshellv1/openshell.pb.go | 22553 ---------------- sdk/go/proto/openshellv1/openshell_grpc.pb.go | 3753 --- sdk/go/proto/optionsv1/options.pb.go | 205 - sdk/go/proto/sandboxv1/sandbox.pb.go | 3101 --- .../compatibility/sandbox_updates_test.go | 121 - tasks/go.toml | 220 - 269 files changed, 37 insertions(+), 77181 deletions(-) delete mode 100644 .github/workflows/sdk-go-tag.yml delete mode 100644 sdk/go/.golangci.yml delete mode 100644 sdk/go/README.md delete mode 100644 sdk/go/buf.gen.yaml delete mode 100644 sdk/go/docs/book.toml delete mode 100644 sdk/go/docs/src/SUMMARY.md delete mode 100644 sdk/go/docs/src/api/client.md delete mode 100644 sdk/go/docs/src/api/config.md delete mode 100644 sdk/go/docs/src/api/edge.md delete mode 100644 sdk/go/docs/src/api/exec.md delete mode 100644 sdk/go/docs/src/api/fake.md delete mode 100644 sdk/go/docs/src/api/files.md delete mode 100644 sdk/go/docs/src/api/gateway.md delete mode 100644 sdk/go/docs/src/api/health.md delete mode 100644 sdk/go/docs/src/api/oidc.md delete mode 100644 sdk/go/docs/src/api/overview.md delete mode 100644 sdk/go/docs/src/api/policy.md delete mode 100644 sdk/go/docs/src/api/profiles.md delete mode 100644 sdk/go/docs/src/api/providers.md delete mode 100644 sdk/go/docs/src/api/refresh.md delete mode 100644 sdk/go/docs/src/api/sandbox-templates.md delete mode 100644 sdk/go/docs/src/api/sandboxes.md delete mode 100644 sdk/go/docs/src/api/services.md delete mode 100644 sdk/go/docs/src/api/ssh.md delete mode 100644 sdk/go/docs/src/api/tcp.md delete mode 100644 sdk/go/docs/src/architecture.md delete mode 100644 sdk/go/docs/src/error-handling.md delete mode 100644 sdk/go/docs/src/getting-started.md delete mode 100644 sdk/go/docs/src/introduction.md delete mode 100644 sdk/go/docs/src/testing.md delete mode 100644 sdk/go/docs/theme/custom.css delete mode 100644 sdk/go/go.mod delete mode 100644 sdk/go/go.sum delete mode 100644 sdk/go/mise.toml delete mode 100644 sdk/go/openshell/v1/auth.go delete mode 100644 sdk/go/openshell/v1/auth_extra.go delete mode 100644 sdk/go/openshell/v1/auth_extra_test.go delete mode 100644 sdk/go/openshell/v1/auth_refresh.go delete mode 100644 sdk/go/openshell/v1/auth_refresh_test.go delete mode 100644 sdk/go/openshell/v1/auth_test.go delete mode 100644 sdk/go/openshell/v1/client.go delete mode 100644 sdk/go/openshell/v1/client_test.go delete mode 100644 sdk/go/openshell/v1/config.go delete mode 100644 sdk/go/openshell/v1/config_client.go delete mode 100644 sdk/go/openshell/v1/config_client_test.go delete mode 100644 sdk/go/openshell/v1/context_errors.go delete mode 100644 sdk/go/openshell/v1/context_errors_test.go delete mode 100644 sdk/go/openshell/v1/doc.go delete mode 100644 sdk/go/openshell/v1/edge/cloudflare.go delete mode 100644 sdk/go/openshell/v1/edge/cloudflare_test.go delete mode 100644 sdk/go/openshell/v1/edge/doc.go delete mode 100644 sdk/go/openshell/v1/edge/options_nil_test.go delete mode 100644 sdk/go/openshell/v1/edge/tunnel.go delete mode 100644 sdk/go/openshell/v1/edge/tunnel_test.go delete mode 100644 sdk/go/openshell/v1/errors.go delete mode 100644 sdk/go/openshell/v1/errors_test.go delete mode 100644 sdk/go/openshell/v1/example_fake_test.go delete mode 100644 sdk/go/openshell/v1/example_test.go delete mode 100644 sdk/go/openshell/v1/exec.go delete mode 100644 sdk/go/openshell/v1/exec_client.go delete mode 100644 sdk/go/openshell/v1/exec_client_test.go delete mode 100644 sdk/go/openshell/v1/exec_control_test.go delete mode 100644 sdk/go/openshell/v1/fake/broadcaster.go delete mode 100644 sdk/go/openshell/v1/fake/broadcaster_test.go delete mode 100644 sdk/go/openshell/v1/fake/config.go delete mode 100644 sdk/go/openshell/v1/fake/config_test.go delete mode 100644 sdk/go/openshell/v1/fake/doc.go delete mode 100644 sdk/go/openshell/v1/fake/exec.go delete mode 100644 sdk/go/openshell/v1/fake/exec_test.go delete mode 100644 sdk/go/openshell/v1/fake/fake.go delete mode 100644 sdk/go/openshell/v1/fake/fake_test.go delete mode 100644 sdk/go/openshell/v1/fake/file.go delete mode 100644 sdk/go/openshell/v1/fake/file_test.go delete mode 100644 sdk/go/openshell/v1/fake/health.go delete mode 100644 sdk/go/openshell/v1/fake/health_test.go delete mode 100644 sdk/go/openshell/v1/fake/mutations.go delete mode 100644 sdk/go/openshell/v1/fake/options_nil_test.go delete mode 100644 sdk/go/openshell/v1/fake/pager.go delete mode 100644 sdk/go/openshell/v1/fake/policy.go delete mode 100644 sdk/go/openshell/v1/fake/policy_test.go delete mode 100644 sdk/go/openshell/v1/fake/profile.go delete mode 100644 sdk/go/openshell/v1/fake/profile_test.go delete mode 100644 sdk/go/openshell/v1/fake/provider.go delete mode 100644 sdk/go/openshell/v1/fake/provider_test.go delete mode 100644 sdk/go/openshell/v1/fake/refresh.go delete mode 100644 sdk/go/openshell/v1/fake/refresh_test.go delete mode 100644 sdk/go/openshell/v1/fake/sandbox.go delete mode 100644 sdk/go/openshell/v1/fake/sandbox_template.go delete mode 100644 sdk/go/openshell/v1/fake/sandbox_template_test.go delete mode 100644 sdk/go/openshell/v1/fake/sandbox_test.go delete mode 100644 sdk/go/openshell/v1/fake/service.go delete mode 100644 sdk/go/openshell/v1/fake/service_test.go delete mode 100644 sdk/go/openshell/v1/fake/ssh.go delete mode 100644 sdk/go/openshell/v1/fake/ssh_test.go delete mode 100644 sdk/go/openshell/v1/fake/store.go delete mode 100644 sdk/go/openshell/v1/fake/store_test.go delete mode 100644 sdk/go/openshell/v1/fake/tcp.go delete mode 100644 sdk/go/openshell/v1/fake/tcp_test.go delete mode 100644 sdk/go/openshell/v1/fake/workspace.go delete mode 100644 sdk/go/openshell/v1/fake/workspace_test.go delete mode 100644 sdk/go/openshell/v1/file.go delete mode 100644 sdk/go/openshell/v1/file_client.go delete mode 100644 sdk/go/openshell/v1/file_client_test.go delete mode 100644 sdk/go/openshell/v1/gateway/config.go delete mode 100644 sdk/go/openshell/v1/gateway/config_test.go delete mode 100644 sdk/go/openshell/v1/gateway/doc.go delete mode 100644 sdk/go/openshell/v1/gateway/errors.go delete mode 100644 sdk/go/openshell/v1/gateway/errors_test.go delete mode 100644 sdk/go/openshell/v1/gateway/gateway.go delete mode 100644 sdk/go/openshell/v1/gateway/gateway_test.go delete mode 100644 sdk/go/openshell/v1/gateway/options.go delete mode 100644 sdk/go/openshell/v1/gateway/options_nil_test.go delete mode 100644 sdk/go/openshell/v1/gateway/paths.go delete mode 100644 sdk/go/openshell/v1/gateway/paths_test.go delete mode 100644 sdk/go/openshell/v1/gateway/token.go delete mode 100644 sdk/go/openshell/v1/gateway/token_test.go delete mode 100644 sdk/go/openshell/v1/health.go delete mode 100644 sdk/go/openshell/v1/health_client.go delete mode 100644 sdk/go/openshell/v1/health_client_test.go delete mode 100644 sdk/go/openshell/v1/integration_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/copy.go delete mode 100644 sdk/go/openshell/v1/internal/converter/coverage_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/errors.go delete mode 100644 sdk/go/openshell/v1/internal/converter/errors_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/exec.go delete mode 100644 sdk/go/openshell/v1/internal/converter/exec_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/health.go delete mode 100644 sdk/go/openshell/v1/internal/converter/health_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/log.go delete mode 100644 sdk/go/openshell/v1/internal/converter/log_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/network_policy.go delete mode 100644 sdk/go/openshell/v1/internal/converter/network_policy_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/policy.go delete mode 100644 sdk/go/openshell/v1/internal/converter/policy_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/profile.go delete mode 100644 sdk/go/openshell/v1/internal/converter/profile_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/provider.go delete mode 100644 sdk/go/openshell/v1/internal/converter/provider_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/refresh.go delete mode 100644 sdk/go/openshell/v1/internal/converter/refresh_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/sandbox.go delete mode 100644 sdk/go/openshell/v1/internal/converter/sandbox_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/service.go delete mode 100644 sdk/go/openshell/v1/internal/converter/service_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/setting.go delete mode 100644 sdk/go/openshell/v1/internal/converter/setting_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/ssh.go delete mode 100644 sdk/go/openshell/v1/internal/converter/ssh_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/time.go delete mode 100644 sdk/go/openshell/v1/internal/converter/time_test.go delete mode 100644 sdk/go/openshell/v1/internal/converter/workspace.go delete mode 100644 sdk/go/openshell/v1/internal/converter/workspace_test.go delete mode 100644 sdk/go/openshell/v1/internal/grpc/conn.go delete mode 100644 sdk/go/openshell/v1/internal/grpc/conn_test.go delete mode 100644 sdk/go/openshell/v1/internal/oidctoken/oidctoken.go delete mode 100644 sdk/go/openshell/v1/internal/options/options.go delete mode 100644 sdk/go/openshell/v1/internal/options/options_test.go delete mode 100644 sdk/go/openshell/v1/logger.go delete mode 100644 sdk/go/openshell/v1/mutations.go delete mode 100644 sdk/go/openshell/v1/oidc/authcode.go delete mode 100644 sdk/go/openshell/v1/oidc/authcode_test.go delete mode 100644 sdk/go/openshell/v1/oidc/browser.go delete mode 100644 sdk/go/openshell/v1/oidc/browser_test.go delete mode 100644 sdk/go/openshell/v1/oidc/credentials.go delete mode 100644 sdk/go/openshell/v1/oidc/credentials_auth.go delete mode 100644 sdk/go/openshell/v1/oidc/credentials_test.go delete mode 100644 sdk/go/openshell/v1/oidc/device.go delete mode 100644 sdk/go/openshell/v1/oidc/device_test.go delete mode 100644 sdk/go/openshell/v1/oidc/discovery.go delete mode 100644 sdk/go/openshell/v1/oidc/discovery_test.go delete mode 100644 sdk/go/openshell/v1/oidc/doc.go delete mode 100644 sdk/go/openshell/v1/oidc/errors.go delete mode 100644 sdk/go/openshell/v1/oidc/errors_test.go delete mode 100644 sdk/go/openshell/v1/oidc/example_test.go delete mode 100644 sdk/go/openshell/v1/oidc/keyboard.go delete mode 100644 sdk/go/openshell/v1/oidc/keyboard_test.go delete mode 100644 sdk/go/openshell/v1/oidc/oidc.go delete mode 100644 sdk/go/openshell/v1/oidc/oidc_test.go delete mode 100644 sdk/go/openshell/v1/oidc/options.go delete mode 100644 sdk/go/openshell/v1/oidc/options_nil_test.go delete mode 100644 sdk/go/openshell/v1/oidc/options_test.go delete mode 100644 sdk/go/openshell/v1/oidc/token.go delete mode 100644 sdk/go/openshell/v1/oidc/token_test.go delete mode 100644 sdk/go/openshell/v1/options.go delete mode 100644 sdk/go/openshell/v1/options_nil_test.go delete mode 100644 sdk/go/openshell/v1/pager.go delete mode 100644 sdk/go/openshell/v1/pager_test.go delete mode 100644 sdk/go/openshell/v1/policy.go delete mode 100644 sdk/go/openshell/v1/policy_client.go delete mode 100644 sdk/go/openshell/v1/policy_client_test.go delete mode 100644 sdk/go/openshell/v1/profile.go delete mode 100644 sdk/go/openshell/v1/profile_client.go delete mode 100644 sdk/go/openshell/v1/profile_client_test.go delete mode 100644 sdk/go/openshell/v1/provider.go delete mode 100644 sdk/go/openshell/v1/provider_client.go delete mode 100644 sdk/go/openshell/v1/provider_client_test.go delete mode 100644 sdk/go/openshell/v1/refresh.go delete mode 100644 sdk/go/openshell/v1/refresh_client.go delete mode 100644 sdk/go/openshell/v1/refresh_client_test.go delete mode 100644 sdk/go/openshell/v1/routing.go delete mode 100644 sdk/go/openshell/v1/routing_test.go delete mode 100644 sdk/go/openshell/v1/sandbox.go delete mode 100644 sdk/go/openshell/v1/sandbox_client.go delete mode 100644 sdk/go/openshell/v1/sandbox_client_test.go delete mode 100644 sdk/go/openshell/v1/sandbox_template.go delete mode 100644 sdk/go/openshell/v1/sandbox_template_client.go delete mode 100644 sdk/go/openshell/v1/sandbox_template_client_test.go delete mode 100644 sdk/go/openshell/v1/service.go delete mode 100644 sdk/go/openshell/v1/service_client.go delete mode 100644 sdk/go/openshell/v1/service_client_test.go delete mode 100644 sdk/go/openshell/v1/ssh.go delete mode 100644 sdk/go/openshell/v1/ssh_client.go delete mode 100644 sdk/go/openshell/v1/ssh_client_test.go delete mode 100644 sdk/go/openshell/v1/tcp.go delete mode 100644 sdk/go/openshell/v1/tcp_client.go delete mode 100644 sdk/go/openshell/v1/tcp_client_test.go delete mode 100644 sdk/go/openshell/v1/types.go delete mode 100644 sdk/go/openshell/v1/types/auth.go delete mode 100644 sdk/go/openshell/v1/types/config.go delete mode 100644 sdk/go/openshell/v1/types/doc.go delete mode 100644 sdk/go/openshell/v1/types/errors.go delete mode 100644 sdk/go/openshell/v1/types/exec.go delete mode 100644 sdk/go/openshell/v1/types/health.go delete mode 100644 sdk/go/openshell/v1/types/log.go delete mode 100644 sdk/go/openshell/v1/types/logger.go delete mode 100644 sdk/go/openshell/v1/types/mutations.go delete mode 100644 sdk/go/openshell/v1/types/network_policy.go delete mode 100644 sdk/go/openshell/v1/types/options.go delete mode 100644 sdk/go/openshell/v1/types/options_nil_test.go delete mode 100644 sdk/go/openshell/v1/types/options_test.go delete mode 100644 sdk/go/openshell/v1/types/policy.go delete mode 100644 sdk/go/openshell/v1/types/profile.go delete mode 100644 sdk/go/openshell/v1/types/provider.go delete mode 100644 sdk/go/openshell/v1/types/refresh.go delete mode 100644 sdk/go/openshell/v1/types/sandbox.go delete mode 100644 sdk/go/openshell/v1/types/service.go delete mode 100644 sdk/go/openshell/v1/types/setting.go delete mode 100644 sdk/go/openshell/v1/types/ssh.go delete mode 100644 sdk/go/openshell/v1/types/types.go delete mode 100644 sdk/go/openshell/v1/types/watch.go delete mode 100644 sdk/go/openshell/v1/types/workspace.go delete mode 100644 sdk/go/openshell/v1/types_reexport.go delete mode 100644 sdk/go/openshell/v1/watch.go delete mode 100644 sdk/go/openshell/v1/watch_test.go delete mode 100644 sdk/go/openshell/v1/workspace.go delete mode 100644 sdk/go/openshell/v1/workspace_client.go delete mode 100644 sdk/go/openshell/v1/workspace_scope.go delete mode 100644 sdk/go/openshell/v1/workspace_test.go delete mode 100644 sdk/go/proto/datamodelv1/datamodel.pb.go delete mode 100644 sdk/go/proto/openshellv1/openshell.pb.go delete mode 100644 sdk/go/proto/openshellv1/openshell_grpc.pb.go delete mode 100644 sdk/go/proto/optionsv1/options.pb.go delete mode 100644 sdk/go/proto/sandboxv1/sandbox.pb.go delete mode 100644 sdk/go/tests/compatibility/sandbox_updates_test.go delete mode 100644 tasks/go.toml diff --git a/.agents/skills/build-openshell-mxc-windows/SKILL.md b/.agents/skills/build-openshell-mxc-windows/SKILL.md index c1df4cc3c8..80978f66b6 100644 --- a/.agents/skills/build-openshell-mxc-windows/SKILL.md +++ b/.agents/skills/build-openshell-mxc-windows/SKILL.md @@ -312,10 +312,9 @@ compatibility under emulation is not part of these tasks. The aggregate commands above on an ARM64 host. The repository-wide `mise run pre-commit` task is also supported on Windows. -Run `rust:lockfiles:check`, `sdk:ts:ci`, and `go:ci` through the Windows-aware -tasks when validating those surfaces. Do not count the Go -Windows ARM64 race-detector exclusion or POSIX permission-bit skips as security -coverage. SDK test dependencies must remain at their lockfile versions. +Run `rust:lockfiles:check` and `sdk:ts:ci` through the Windows-aware tasks +when validating those surfaces. Do not count POSIX permission-bit skips as +security coverage. SDK test dependencies must remain at their lockfile versions. Its Rust check, Clippy, and test dependencies enter the same MSVC environment for the native host target and use an inherited compiler wrapper when it is available. Linux glibc diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml index 5fc0887272..0c2dfb0503 100644 --- a/.github/codeql/codeql-config.yml +++ b/.github/codeql/codeql-config.yml @@ -6,7 +6,6 @@ name: OpenShell Rust and SDKs paths: - crates - examples - - sdk/go - sdk/typescript/src - python/openshell diff --git a/.github/workflows/branch-checks.yml b/.github/workflows/branch-checks.yml index 004e181b2c..6b3e8fd0ad 100644 --- a/.github/workflows/branch-checks.yml +++ b/.github/workflows/branch-checks.yml @@ -608,39 +608,6 @@ jobs: - name: Test release qualification summary run: mise run test:qualification-summary - sdk-go-freeze: - # The Go SDK moved to ai-openshell/sdk-go (#5031). Its directory stays only - # until the final deprecated sdk/go tag exists, so PRs must not add or change - # files in it. Deleting files is allowed (the removal PR), and so are the - # moved notice and the deprecation comment in README.md and go.mod. - # Merge queue runs skip this job; the PR already passed it on its mirror. - name: Go SDK Freeze - needs: pr_metadata - if: needs.pr_metadata.outputs.should_run == 'true' && github.event_name == 'push' - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Reject changes to sdk/go - env: - BASE_SHA: ${{ needs.pr_metadata.outputs.base_sha }} - run: | - set -euo pipefail - changed=$(git diff --name-only --diff-filter=d "${BASE_SHA}...HEAD" -- \ - sdk/go ':!sdk/go/README.md' ':!sdk/go/go.mod') - if [ -z "$changed" ]; then - echo "No added or changed files under sdk/go." - exit 0 - fi - echo "::error title=Go SDK moved::The Go SDK moved to https://github.com/ai-openshell/sdk-go. Drop the sdk/go changes from this PR and open SDK changes there. Regenerated bindings are not needed here; sdk-go regenerates them from core releases." - echo "Changed files under sdk/go:" - echo "$changed" - exit 1 - python-sdk-freeze: # The Python SDK moved to ai-openshell/sdk-python (#5287), which imported it # at v0.1.7. Its files stay only until core removes them, so PRs must not diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4eec0fa868..7607160b99 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -40,8 +40,6 @@ jobs: include: - language: rust build-mode: none - - language: go - build-mode: manual - language: python build-mode: none - language: javascript-typescript @@ -53,13 +51,6 @@ jobs: ref: ${{ inputs.candidate_ref && format('refs/tags/{0}', inputs.candidate_ref) || '' }} persist-credentials: false - - name: Set up Go - if: matrix.language == 'go' - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 - with: - go-version-file: sdk/go/go.mod - cache-dependency-path: sdk/go/go.sum - - name: Initialize CodeQL uses: github/codeql-action/init@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3 with: @@ -67,11 +58,6 @@ jobs: build-mode: ${{ matrix.build-mode }} config-file: ./.github/codeql/codeql-config.yml - - name: Build Go SDK - if: matrix.language == 'go' - working-directory: sdk/go - run: go build ./... - - name: Analyze id: analyze uses: github/codeql-action/analyze@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3 diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index c4e9331587..dc1d8a107d 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -543,17 +543,6 @@ jobs: npm-version: ${{ inputs.npm-version }} make-latest: ${{ inputs.make-latest }} - tag-sdk-go: - name: Tag Go SDK - needs: release - if: ${{ !inputs.is-prerelease }} - permissions: - contents: write - uses: ./.github/workflows/sdk-go-tag.yml - with: - version: ${{ inputs.semver }} - source-sha: ${{ inputs.source-sha }} - # GitHub treats skipped jobs as passing, so a publisher that never ran would # leave the run green. Fail when any publisher expected for this release kind # did not succeed, so callers' failure notifications fire. @@ -567,7 +556,6 @@ jobs: - publish-snap - publish-prerelease-snap - publish-sdk-typescript - - tag-sdk-go if: always() runs-on: ubuntu-latest timeout-minutes: 2 @@ -583,7 +571,7 @@ jobs: if [[ "${IS_PRERELEASE}" == "true" ]]; then expected+=(publish-prerelease-snap) else - expected+=(publish-sdk-typescript tag-sdk-go) + expected+=(publish-sdk-typescript) if [[ "${MAKE_LATEST}" == "true" ]]; then expected+=(publish-snap) fi diff --git a/.github/workflows/sdk-go-tag.yml b/.github/workflows/sdk-go-tag.yml deleted file mode 100644 index 38af6507bd..0000000000 --- a/.github/workflows/sdk-go-tag.yml +++ /dev/null @@ -1,58 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -# Go resolves submodule versions from directory-prefixed tags, so the SDK at -# sdk/go needs its own sdk/go/vX.Y.Z tag to show up as a release on pkg.go.dev. -name: Tag Go SDK - -on: - workflow_call: - inputs: - version: - required: true - type: string - source-sha: - required: true - type: string - workflow_dispatch: - inputs: - version: - description: Release version without the v prefix, e.g. 0.1.6 - required: true - type: string - source-sha: - description: Commit the stable tag points to - required: true - type: string - -permissions: - contents: write - -jobs: - tag: - name: Tag Go SDK - runs-on: ubuntu-latest - timeout-minutes: 5 - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VERSION: ${{ inputs.version }} - SOURCE_SHA: ${{ inputs.source-sha }} - steps: - - name: Create Go SDK tag - shell: bash - run: | - set -euo pipefail - tag="sdk/go/v${VERSION}" - # Lightweight tag, so the ref points at the commit directly. - if ! existing=$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${tag}" --jq .object.sha 2>/dev/null); then - gh api "repos/${GITHUB_REPOSITORY}/git/refs" -f ref="refs/tags/${tag}" -f sha="${SOURCE_SHA}" - elif [[ "${existing}" != "${SOURCE_SHA}" ]]; then - echo "${tag} already exists at ${existing}, expected ${SOURCE_SHA}" >&2 - exit 1 - fi - - # pkg.go.dev picks up versions the Go module proxy has fetched. - - name: Request version from Go module proxy - continue-on-error: true - shell: bash - run: curl --fail --silent --show-error --max-time 60 "https://proxy.golang.org/github.com/!n!v!i!d!i!a/!open!shell/sdk/go/@v/v${VERSION}.info" diff --git a/AGENTS.md b/AGENTS.md index 4760164e97..012969da9f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -196,10 +196,9 @@ Follow `CONTRIBUTING.md` and `create-github-pr`. Authors without target-reposito - Sign off on each commit for DCO compliance. Use the `--signoff` option to `git commit` to add the `Signed-off-by` footer to ensure the user's configured email address is used. - Never mention Claude or any AI agent in commits, PR descriptions, or issues (no author attribution, no Co-Authored-By, no "Generated with" footers) -## Go SDK (`sdk/go/`) +## Go SDK -- The Go SDK moved to [ai-openshell/sdk-go](https://github.com/ai-openshell/sdk-go) (module `github.com/ai-openshell/sdk-go`). Open SDK changes there. -- `sdk/go/` is frozen until it is removed; Branch Checks fails PRs that change it. Proto changes do not need regenerated Go bindings. +- The Go SDK lives in [ai-openshell/sdk-go](https://github.com/ai-openshell/sdk-go) (module `github.com/ai-openshell/sdk-go`). Open SDK changes, including dependency and security updates, there; proto changes here need no Go bindings. ## Python SDK (`python/openshell/`) diff --git a/CI.md b/CI.md index 6dac0a4bf5..90fe3fae0e 100644 --- a/CI.md +++ b/CI.md @@ -732,7 +732,6 @@ Each step that publishes to an external registry, store, or site lives in its ow | `.github/workflows/snap-package.yml` | Builds snap and component artifacts for Release Dev, Release, and Pre-release without Store credentials or publication. | | `.github/workflows/snap-publish.yml` | Uploads existing snap and component artifacts directly to a Store channel without rebuilding. Release Dev uses `latest/edge`, Pre-release uses `latest/beta`, and Release uses `latest/stable` for the stable rebuild. Uploads queue per channel and architecture, so tagged beta and stable jobs recheck repository tags after earlier publication finishes and before updating their floating channels. Manual dispatch uploads the `snap-linux-*` artifacts from `artifact-run-id`. | | `.github/workflows/sdk-typescript-publish.yml` | Publishes the TypeScript SDK to GitHub Packages. Called by stable Release; manual dispatch publishes a given tag and npm version. | -| `.github/workflows/sdk-go-tag.yml` | Creates the `sdk/go/vX.Y.Z` tag and asks the Go module proxy to fetch it. Called by stable Release; manual dispatch tags a given version and commit. | | `.github/workflows/release-canary.yml` | Smoke-tests published dev artifacts in the `macos`, `ubuntu-deb`, `ubuntu-snap-system-docker`, `fedora`, and `kubernetes` (kind + Helm) jobs. Each job reaches its gateway and creates, exercises, and deletes a sandbox. The `ubuntu-snap-system-docker` job verifies a compatible system Docker lifecycle, while `ubuntu-snap-docker-preflight` tests fail-fast behavior when Docker is absent or supplied by the Docker snap. The positive snap lane also runs a local policy containment check with the packaged prover. It runs automatically after `Release Dev` succeeds and supports manual dispatch (`gh workflow run release-canary.yml --ref `). See the `test-release-canary` skill for the playbook and local kind reproduction. | ## Required status contexts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e238089b18..f971884fdf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -383,7 +383,6 @@ These are the primary `mise` tasks for day-to-day development: | `crates/openshell-policy-schema/` | Canonical authored policy DTOs and bounded YAML/JSON parser | | `crates/openshell-prover-cli/` | Standalone local policy boundary checker | | `python/` | Python SDK (frozen, moved to ai-openshell/sdk-python) | -| `sdk/go/` | Go SDK (types, gRPC clients, converters) | | `sdk/typescript/` | TypeScript SDK (Connect client and generated protobuf bindings) | | `proto/` | Protocol buffer definitions and [public API conventions](proto/README.md) | | `tasks/` | `mise` task definitions and build scripts | diff --git a/README.md b/README.md index 072e83f13b..ea31d96813 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ SDKs connect applications to an OpenShell gateway. They do not install the CLI. |---|---|---| | Python | `uv add openshell` | [README](python/openshell/) | | TypeScript | `npm install @nvidia/openshell-sdk` (GitHub Packages) | [README](sdk/typescript/README.md) | -| Go | `go get github.com/NVIDIA/OpenShell/sdk/go@latest` | [README](sdk/go/README.md) | +| Go | `go get github.com/ai-openshell/sdk-go@vX.Y.Z` | [ai-openshell/sdk-go](https://github.com/ai-openshell/sdk-go) | | Rust | `cargo add openshell-sdk --git https://github.com/NVIDIA/OpenShell --tag ` | [Installation and usage](docs/sdk/rust.mdx) | ## Community diff --git a/TESTING.md b/TESTING.md index 2250118e6e..1541964ea0 100644 --- a/TESTING.md +++ b/TESTING.md @@ -124,8 +124,7 @@ plugin through its Windows `.cmd` launcher, and installs the matching locked ARM64 Rolldown binding when Node itself is ARM64. The helper preserves lockfile resolution; it must not upgrade unrelated test dependencies. -`mise run --skip-tools go:ci` retains race detection except on Windows ARM64, -where Go does not support it. Windows token-file tests explicitly skip POSIX +Windows token-file tests explicitly skip POSIX mode-bit assertions; those skips do not establish Windows ACL protection. Use a checkout with LF text files when running Unix-shell fixture checks. diff --git a/buf.yaml b/buf.yaml index 4d6ac0b4de..b506c9b103 100644 --- a/buf.yaml +++ b/buf.yaml @@ -3,9 +3,9 @@ # Repo-level buf workspace. Declares public and storage modules so buf generate, # buf lint, buf breaking, and the editor LSP all resolve imports the same way. -# Code generation lives with each consumer (see sdk/go/buf.gen.yaml, -# sdk/typescript/buf.gen.yaml); this file owns the module boundaries and proto -# validation policy. +# Code generation lives with each consumer (see sdk/typescript/buf.gen.yaml; +# the Go SDK in ai-openshell/sdk-go generates from a release tag); this file +# owns the module boundaries and proto validation policy. version: v2 modules: - path: proto diff --git a/docs/sdk/go.mdx b/docs/sdk/go.mdx index 5c7779a142..610fee64ee 100644 --- a/docs/sdk/go.mdx +++ b/docs/sdk/go.mdx @@ -10,14 +10,18 @@ position: 1 Use the Go SDK to manage OpenShell resources from Go applications, operators, and controllers. Its typed subclients follow familiar Kubernetes client -patterns. Use the SDK and gateway from the same OpenShell release when possible. +patterns. The SDK is developed in +[ai-openshell/sdk-go](https://github.com/ai-openshell/sdk-go), which also has +the full [SDK documentation](https://ai-openshell.github.io/sdk-go/). ## Install the SDK -The SDK requires Go 1.25.13 or later. Add it to your Go module: +The SDK requires Go 1.26 or later. SDK releases follow OpenShell releases, +so SDK `vX.Y.Z` works with a gateway from OpenShell `vX.Y.Z`. Add the version +that matches your gateway to your Go module: ```shell -go get github.com/NVIDIA/OpenShell/sdk/go@latest +go get github.com/ai-openshell/sdk-go@vX.Y.Z ``` ## Connect to a Gateway @@ -34,7 +38,7 @@ import ( "log" "time" - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" + v1 "github.com/ai-openshell/sdk-go/openshell/v1" ) func main() { @@ -70,7 +74,7 @@ client, err := v1.NewClient(v1.Config{ Omit `CAFile` to use system roots. For renewable OIDC service credentials, use `oidc.NewClientCredentialsAuth` from -`github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/oidc`. +`github.com/ai-openshell/sdk-go/openshell/v1/oidc`. ## Discover the Active Gateway @@ -95,7 +99,6 @@ sandbox, err := client.Sandboxes().Create( Image: "registry.example.com/team/python-agent:1.0", }, }, - nil, ) if err != nil { log.Fatal(err) @@ -126,8 +129,24 @@ The root client also exposes subclients for providers, services, files, SSH, TCP forwarding, policy, configuration, templates, and workspaces. List methods return lazy pagers so callers choose when to fetch the next page. +## Migrate from `github.com/NVIDIA/OpenShell/sdk/go` + +The SDK used to live in this repository as `github.com/NVIDIA/OpenShell/sdk/go`. +That module is deprecated and gets no further changes, including dependency and +security updates. As of the move, packages and APIs are the same; only the +module path is new. Rewrite the imports, then fetch the SDK version that +matches your gateway: + +```shell +grep -rlZ --include='*.go' --include=go.mod 'github.com/NVIDIA/OpenShell/sdk/go' . | xargs -0 perl -pi -e 's#github.com/NVIDIA/OpenShell/sdk/go#github.com/ai-openshell/sdk-go#g' +go get github.com/ai-openshell/sdk-go@vX.Y.Z && go mod tidy +``` + +Existing builds keep working: the old module's tags stay published, and the +latest one carries the deprecation notice that `go get` and pkg.go.dev show. + ## Next Steps - Review [Gateway Authentication](/how-it-works/gateways/authentication) before connecting a service to a production gateway. - Review [API Errors](/sdk/api-errors) for the gateway error model. -- Browse the [Go package reference](https://pkg.go.dev/github.com/NVIDIA/OpenShell/sdk/go/openshell/v1) for all exported types and methods. +- Browse the [Go package reference](https://pkg.go.dev/github.com/ai-openshell/sdk-go/openshell/v1) for all exported types and methods. diff --git a/mise.lock b/mise.lock index ec8cccf089..c38d1c2d0a 100644 --- a/mise.lock +++ b/mise.lock @@ -259,46 +259,6 @@ checksum = "sha256:83a7d5955c7ac96ede5d896ac9ede5f7ecce9ece0e95d9e47acd766b09e2e url = "https://github.com/rust-secure-code/cargo-auditable/releases/download/v0.7.5/cargo-auditable-x86_64-pc-windows-msvc.zip" url_api = "https://api.github.com/repos/rust-secure-code/cargo-auditable/releases/assets/426566657" -[[tools.go]] -version = "1.26.7" -backend = "core:go" - -[tools.go."platforms.linux-arm64"] -checksum = "sha256:5a4ec883379d51ee9ce1040d5e87f8d35e20387574dd8c947feb01eabc3c1b37" -url = "https://dl.google.com/go/go1.26.7.linux-arm64.tar.gz" - -[tools.go."platforms.linux-x64"] -checksum = "sha256:ffb5f8de10c62550dfddab66b36b57030721e0a44a3218e9e1181d7b59f121ca" -url = "https://dl.google.com/go/go1.26.7.linux-amd64.tar.gz" - -[tools.go."platforms.macos-arm64"] -checksum = "sha256:020a1e8224811be75163e920bc77e0926a1390a6aeea19bdcf23f74b9d749f6d" -url = "https://dl.google.com/go/go1.26.7.darwin-arm64.tar.gz" - -[tools.go."platforms.windows-arm64"] -checksum = "sha256:6f1b08de9e2dd94f69c52e524ab6834737275253291e8fd7f1c12ed4eceeda89" -url = "https://dl.google.com/go/go1.26.7.windows-arm64.zip" - -[tools.go."platforms.windows-x64"] -checksum = "sha256:f4f534a486e4bc3387fa18f08208f2f854b7aaea8a08f2a2d829a914a05abb11" -url = "https://dl.google.com/go/go1.26.7.windows-amd64.zip" - -[[tools."go:github.com/golangci/golangci-lint/v2/cmd/golangci-lint"]] -version = "2.12.2" -backend = "go:github.com/golangci/golangci-lint/v2/cmd/golangci-lint" - -[[tools."go:golang.org/x/tools/cmd/goimports"]] -version = "0.48.0" -backend = "go:golang.org/x/tools/cmd/goimports" - -[[tools."go:google.golang.org/grpc/cmd/protoc-gen-go-grpc"]] -version = "1.6.2" -backend = "go:google.golang.org/grpc/cmd/protoc-gen-go-grpc" - -[[tools."go:google.golang.org/protobuf/cmd/protoc-gen-go"]] -version = "1.36.11" -backend = "go:google.golang.org/protobuf/cmd/protoc-gen-go" - [[tools.helm]] version = "4.2.0" backend = "aqua:helm/helm" diff --git a/mise.toml b/mise.toml index 7a99d5f50b..42470bb824 100644 --- a/mise.toml +++ b/mise.toml @@ -25,11 +25,6 @@ node = "24.15.0" kubectl = { version = "1.36.1", version_prefix = "v" } uv = "0.10.12" protoc = "29.6" -go = "1.26.7" -"go:github.com/golangci/golangci-lint/v2/cmd/golangci-lint" = "2.12" -"go:google.golang.org/protobuf/cmd/protoc-gen-go" = "1.36.11" -"go:google.golang.org/grpc/cmd/protoc-gen-go-grpc" = "1.6.2" -"go:golang.org/x/tools/cmd/goimports" = "0.48.0" buf = "1.72.0" helm = { version = "4.2.0", version_prefix = "v" } helm-docs = "1.14.2" diff --git a/sdk/go/.golangci.yml b/sdk/go/.golangci.yml deleted file mode 100644 index bf33432fb3..0000000000 --- a/sdk/go/.golangci.yml +++ /dev/null @@ -1,43 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -version: "2" - -run: - timeout: 5m - -linters: - enable: - - govet - - errcheck - - staticcheck - - unused - - ineffassign - - revive - - goheader - exclusions: - rules: - - path: "proto/" - linters: - - goheader - - revive - -linters-settings: - goheader: - template: |- - SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - SPDX-License-Identifier: Apache-2.0 - revive: - rules: - - name: blank-imports - - name: exported - - name: var-naming - - name: indent-error-flow - - name: range - - name: error-return - - name: error-naming - - name: error-strings - - name: receiver-naming - - name: increment-decrement - - name: superfluous-else - - name: unreachable-code diff --git a/sdk/go/README.md b/sdk/go/README.md deleted file mode 100644 index ec03b0dd13..0000000000 --- a/sdk/go/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# OpenShell SDK for Go (moved) - -> [!IMPORTANT] -> The Go SDK moved to **[ai-openshell/sdk-go](https://github.com/ai-openshell/sdk-go)**. -> The module path is now `github.com/ai-openshell/sdk-go`. - -This directory is frozen and will be removed from NVIDIA/OpenShell. Its last tagged version, `github.com/NVIDIA/OpenShell/sdk/go`, is marked deprecated and gets no further changes. Open issues and pull requests for the SDK in [ai-openshell/sdk-go](https://github.com/ai-openshell/sdk-go). - -## Migrating - -Packages and APIs are unchanged; only the module path is new. Rewrite the imports, then fetch the new module: - -```shell -grep -rl --include='*.go' --include=go.mod 'github.com/NVIDIA/OpenShell/sdk/go' . | xargs perl -pi -e 's#github.com/NVIDIA/OpenShell/sdk/go#github.com/ai-openshell/sdk-go#g' -go get github.com/ai-openshell/sdk-go@latest && go mod tidy -``` - -SDK releases follow core releases, so SDK `vX.Y.Z` works with core `vX.Y.Z`. The move is tracked in [#5031](https://github.com/NVIDIA/OpenShell/issues/5031). diff --git a/sdk/go/buf.gen.yaml b/sdk/go/buf.gen.yaml deleted file mode 100644 index 3d45c0ace7..0000000000 --- a/sdk/go/buf.gen.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -# Code generation for the Go SDK. buf compiles the proto module with its own -# compiler and runs protoc-gen-go / protoc-gen-go-grpc from mise-managed -# binaries. The public SDK entry points are declared here; imported schemas -# are included automatically, so their canonical dependency closure is -# generated without duplicating a filename list in the generation scripts. -version: v2 - -inputs: - - proto_file: proto/openshell.proto - -plugins: - - local: protoc-gen-go - out: sdk/go - include_imports: true - opt: - - module=github.com/NVIDIA/OpenShell/sdk/go - - Mopenshell.proto=github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1 - - Mdatamodel.proto=github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1 - - Msandbox.proto=github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1 - - Moptions.proto=github.com/NVIDIA/OpenShell/sdk/go/proto/optionsv1 - - local: protoc-gen-go-grpc - out: sdk/go - include_imports: true - opt: - - module=github.com/NVIDIA/OpenShell/sdk/go - - Mopenshell.proto=github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1 - - Mdatamodel.proto=github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1 - - Msandbox.proto=github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1 - - Moptions.proto=github.com/NVIDIA/OpenShell/sdk/go/proto/optionsv1 diff --git a/sdk/go/docs/book.toml b/sdk/go/docs/book.toml deleted file mode 100644 index 01cfe47ab1..0000000000 --- a/sdk/go/docs/book.toml +++ /dev/null @@ -1,23 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -[book] -title = "OpenShell Go SDK" -authors = ["NVIDIA Corporation"] -language = "en" -src = "src" - -[build] -build-dir = "book" -create-missing = false - -[output.html] -default-theme = "ayu" -preferred-dark-theme = "navy" -additional-css = ["theme/custom.css"] -git-repository-url = "https://github.com/NVIDIA/OpenShell/sdk/go" -edit-url-template = "https://github.com/NVIDIA/OpenShell/sdk/go/edit/main/docs/src/{path}" - -[output.html.search] -enable = true -limit-results = 20 diff --git a/sdk/go/docs/src/SUMMARY.md b/sdk/go/docs/src/SUMMARY.md deleted file mode 100644 index bf500f68eb..0000000000 --- a/sdk/go/docs/src/SUMMARY.md +++ /dev/null @@ -1,38 +0,0 @@ -# Summary - -[Introduction](introduction.md) - -# Getting Started - -- [Quick Start](getting-started.md) - -# Architecture - -- [Overview](architecture.md) - -# API Reference - -- [Overview](api/overview.md) -- [Client](api/client.md) -- [Sandboxes](api/sandboxes.md) -- [Sandbox Templates](api/sandbox-templates.md) -- [Exec](api/exec.md) -- [Providers](api/providers.md) -- [Profiles](api/profiles.md) -- [Refresh](api/refresh.md) -- [Services](api/services.md) -- [Files](api/files.md) -- [Health](api/health.md) -- [SSH](api/ssh.md) -- [TCP](api/tcp.md) -- [Config](api/config.md) -- [Policy](api/policy.md) -- [Gateway](api/gateway.md) -- [OIDC](api/oidc.md) -- [Edge](api/edge.md) -- [Fake](api/fake.md) - -# Guides - -- [Error Handling](error-handling.md) -- [Testing](testing.md) diff --git a/sdk/go/docs/src/api/client.md b/sdk/go/docs/src/api/client.md deleted file mode 100644 index 9fe8b4aed9..0000000000 --- a/sdk/go/docs/src/api/client.md +++ /dev/null @@ -1,79 +0,0 @@ -# Client - -Constructor: `v1.NewClient(config)` - -`Client` is the root entry point for SDK operations. It provides typed accessors -for each resource domain and manages the underlying gRPC connection. The -`ClientInterface` covers the stable core accessors; additive helpers such as -`SandboxTemplates()` are available on the concrete client. - -## Methods - -| Accessor | Returns | Description | -|----------|---------|-------------| -| `Sandboxes()` | `SandboxInterface` | Sandbox lifecycle management | -| `SandboxTemplates()` | `SandboxTemplateInterface` | Reusable sandbox template management | -| `Providers()` | `ProviderInterface` | Provider CRUD and idempotent ensure | -| `Services()` | `ServiceInterface` | Service exposure and management | -| `Exec()` | `ExecInterface` | Command execution (run, stream, interactive) | -| `Files()` | `FileInterface` | File upload and download | -| `Health()` | `HealthInterface` | Gateway health checking | -| `SSH()` | `SSHInterface` | SSH session and tunnel management | -| `TCP()` | `TCPInterface` | TCP port forwarding | -| `Config()` | `ConfigInterface` | Sandbox and gateway configuration | -| `Policy()` | `PolicyInterface` | Draft policy review workflow | -| `Close()` | `error` | Close the gRPC connection | - -Sub-client hierarchy: `Providers()` has two nested accessors: -- `client.Providers().Profiles()` returns `ProfileInterface` -- `client.Providers().Refresh()` returns `RefreshInterface` - -## Creating a Client - -```go -import v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - -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() -``` - -## Configuration - -The `Config` struct controls connection behavior: - -```go -type Config struct { - Address string // Gateway address (host:port) - TLS *TLSConfig // TLS settings (nil uses system defaults) - Auth AuthProvider // Authentication provider - Timeout time.Duration // Default timeout for all operations (0 = no timeout) - RetryPolicy *RetryPolicy // Retry configuration (nil = no automatic retries) - Logger Logger // Custom logger (nil = no logging) -} -``` - -Authentication providers: -- `v1.StaticToken(token)` provides a fixed bearer token -- `v1.NoAuth()` skips authentication (for local development) - -## Testing - -For unit tests, use the fake client instead of a real connection: - -```go -import "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/fake" - -client := fake.NewClient() -defer client.Close() -``` - -The fake client implements the full `ClientInterface` with in-memory stores. -See [Testing](../testing.md) for details. - -See also: [Getting Started](../getting-started.md), [Architecture](../architecture.md) diff --git a/sdk/go/docs/src/api/config.md b/sdk/go/docs/src/api/config.md deleted file mode 100644 index 601f1a86a7..0000000000 --- a/sdk/go/docs/src/api/config.md +++ /dev/null @@ -1,52 +0,0 @@ -# Config - -Accessor: `client.Config()` - -Retrieve and update configuration for sandboxes and the gateway. - -## GetSandbox - -Retrieve the current configuration for a specific sandbox. - -```go -config, err := client.Config().GetSandbox(ctx, "default", "sandbox-123") -if err != nil { - log.Fatal(err) -} -fmt.Printf("Sandbox config: policy_version=%d, revision=%d\n", - config.PolicyVersion, config.ConfigRevision) -``` - -## GetGateway - -Retrieve the gateway-level configuration. - -```go -config, err := client.Config().GetGateway(ctx) -if err != nil { - log.Fatal(err) -} -fmt.Printf("Gateway settings revision: %d\n", config.SettingsRevision) -``` - -## Update - -Apply a configuration update. The update is validated before being applied. - -```go -result, err := client.Config().Update(ctx, "default", &v1.ConfigUpdate{ - Name: "sandbox-123", - SettingKey: "idle_timeout", - SettingValue: &v1.SettingValue{ - Type: v1.SettingValueString, - StringVal: "30m", - }, -}) -if err != nil { - // See [Error Handling](../error-handling.md) for validation errors - log.Fatal(err) -} -fmt.Printf("Config updated: revision=%d\n", result.SettingsRevision) -``` - -See also: [Error Handling](../error-handling.md) diff --git a/sdk/go/docs/src/api/edge.md b/sdk/go/docs/src/api/edge.md deleted file mode 100644 index eba531ad4d..0000000000 --- a/sdk/go/docs/src/api/edge.md +++ /dev/null @@ -1,91 +0,0 @@ -# Edge - -Package: `openshell/v1/edge` - -The edge package provides utilities for connecting to OpenShell gateways -through edge proxies such as Cloudflare Access. It includes auth wrappers -for edge proxy headers and a WebSocket tunnel proxy for gRPC transport -through HTTP/1.1-only proxies. - -## Cloudflare Access - -Wrap any `AuthProvider` with Cloudflare Access headers -(`cf-access-jwt-assertion` and `CF_Authorization` cookie): - -```go -import "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/edge" - -base := v1.StaticToken("my-gateway-token") -auth, err := edge.CloudflareAccess(base, os.Getenv("CF_ACCESS_TOKEN")) -if err != nil { - log.Fatal(err) -} -client, err := v1.NewClient(v1.Config{ - Address: "gateway.example.com:443", - Auth: auth, -}) -``` - -CloudflareAccess composes with any auth provider, including `RefreshableToken` -for automatic token refresh: - -```go -tokenSource := oauth2Config.TokenSource(ctx, initialToken) -refreshAuth, err := v1.RefreshableToken(tokenSource) -if err != nil { - log.Fatal(err) -} -auth, err := edge.CloudflareAccess(refreshAuth, cfToken) -``` - -## WebSocket Tunnel - -`TunnelProxy` bridges gRPC connections over a WebSocket tunnel for edge -proxies that reject standard HTTP/2 POST requests. The tunnel carries -its own edge token for proxy authentication, independent of the -application-level auth provider. - -```go -tunnel, err := edge.NewTunnelProxy( - "wss://gateway.example.com/ws", - os.Getenv("CF_ACCESS_TOKEN"), -) -if err != nil { - log.Fatal(err) -} -defer tunnel.Close() - -auth := v1.StaticToken("my-gateway-token") -client, err := v1.NewClient(v1.Config{ - Address: tunnel.Addr(), - Auth: auth, - TLS: &v1.TLSConfig{Insecure: true}, // local tunnel -}) -``` - -## Functions - -| Function | Description | -|----------|-------------| -| `CloudflareAccess(base, edgeToken)` | Wrap an AuthProvider with Cloudflare Access headers | -| `NewTunnelProxy(url, edgeToken, opts...)` | Create a WebSocket tunnel proxy for gRPC-over-HTTP/1.1 | - -## TunnelProxy Methods - -| Method | Description | -|--------|-------------| -| `Addr()` | Local listener address for gRPC client to dial | -| `Close()` | Gracefully drain in-flight connections and shut down | - -## TunnelOption - -| Constructor | Effect | -|-------------|--------| -| `WithTunnelTLS(cfg)` | Configure TLS for the WebSocket connection | -| `WithTunnelLogger(l)` | Set a logger for tunnel events | -| `WithCloseTimeout(d)` | Override the graceful shutdown timeout (default 5s) | - -## Thread Safety - -All exported functions and methods are safe for concurrent use. -`Close` is idempotent and safe to call multiple times. diff --git a/sdk/go/docs/src/api/exec.md b/sdk/go/docs/src/api/exec.md deleted file mode 100644 index 72ff49b347..0000000000 --- a/sdk/go/docs/src/api/exec.md +++ /dev/null @@ -1,175 +0,0 @@ -# Exec - -Accessor: `client.Exec()` - -Execute commands in a sandbox with three modes: one-shot (`Run`), streaming -(`Stream`), or interactive terminal (`Interactive`). - -## Run - -Execute a command and collect all output into a single result. - -```go -result, err := client.Exec().Run(ctx, "default", "sbx-123", []string{"ls", "-la"}) -if err != nil { - log.Fatal(err) -} - -fmt.Println("Exit code:", result.ExitCode) -fmt.Println("Stdout:", result.Stdout) -fmt.Println("Stderr:", result.Stderr) -``` - -The SDK collects all streamed events, assembles stdout/stderr, and returns a single `ExecResult`. - -`ExecResult` contains the complete output after the command finishes: - -| Field | Type | Description | -|------------|--------|------------------------------| -| `Stdout` | string | Captured standard output | -| `Stderr` | string | Captured standard error | -| `ExitCode` | int | Process exit code | - -## Stream - -Execute a command and process output chunks as they arrive. - -```go -stream, err := client.Exec().Stream(ctx, "default", "sbx-123", []string{"tail", "-f", "/var/log/app.log"}) -if err != nil { - log.Fatal(err) -} -defer stream.Close() - -for { - chunk, err := stream.Next() - if err == io.EOF { - break - } - if err != nil { - log.Fatal(err) - } - - if chunk.Stream == v1.StreamStdout { - fmt.Print(string(chunk.Data)) - } -} - -exitCode, err := stream.ExitCode() -if err != nil { - log.Fatal(err) -} -fmt.Println("Exited with:", exitCode) -``` - -## Interactive - -Open a bidirectional terminal session with a command. - -```go -session, err := client.Exec().Interactive(ctx, "default", "sbx-123", []string{"/bin/bash"}, 80, 24) -if err != nil { - log.Fatal(err) -} -defer session.Close() - -// Send input -_, err = session.Write([]byte("echo hello\n")) -if err != nil { - log.Fatal(err) -} - -// Read output -buf := make([]byte, 4096) -n, err := session.Read(buf) -if err != nil { - log.Fatal(err) -} -fmt.Print(string(buf[:n])) - -// Handle terminal resize -if err := session.Resize(120, 40); err != nil { - log.Fatal(err) -} - -// Get exit code after session ends -exitCode, err := session.ExitCode() -if err != nil { - log.Fatal(err) -} -fmt.Println("Exited with:", exitCode) -``` - -The SDK wraps the bidirectional stream as an `InteractiveSession` with `Read`/`Write`/`Resize` methods. - -## ExecStream - -`ExecStream` provides an iterator interface over command output chunks. Call `Next()` repeatedly to receive output as it is produced. When the command finishes, `Next()` returns `io.EOF`. - -```go -type ExecStream interface { - Next() (*ExecChunk, error) - ExitCode() (int, error) - Close() error -} -``` - -| Method | Description | -|------------|----------------------------------------------------------------| -| `Next` | Returns the next output chunk. Returns `io.EOF` when done. | -| `ExitCode` | Returns the process exit code. Call after `Next` returns `io.EOF`. | -| `Close` | Releases the underlying stream resources. | - -## InteractiveSession - -`InteractiveSession` implements `io.Reader` and `io.Writer` for bidirectional communication with a running process. Use it for terminal emulation, REPL interaction, or any command that requires ongoing input. - -```go -type InteractiveSession interface { - Read(p []byte) (int, error) - Write(p []byte) (int, error) - Resize(cols, rows uint32) error - ExitCode() (int, error) - Close() error -} -``` - -| Method | Description | -|------------|---------------------------------------------------------------------| -| `Read` | Reads output from the process into the provided buffer. | -| `Write` | Sends input to the process. | -| `Resize` | Updates the terminal dimensions (columns and rows). | -| `ExitCode` | Waits for final gRPC status; returns any observed exit code alongside a later stream error. | -| `Close` | Aborts the RPC and releases resources. | - -SDK sessions also implement the optional `InteractiveSessionControl` interface, -which embeds `InteractiveSession` and adds `CloseWrite() error` and `Cancel() error`. -Existing custom implementations only need the original methods above. - -Call `CloseInteractiveInput(session)` to end stdin and resize input without -cancelling output. It invokes `CloseWrite()` when supported; otherwise it returns -`ErrorUnimplemented` without closing the session. Call `CancelInteractive(session)` -to invoke `Cancel()` when supported, falling back to `Close()` for legacy sessions. -The SDK's close and cancel methods are idempotent. - -Drain `Read` concurrently with waiting for `ExitCode`; bounded output buffers can -otherwise prevent completion. After input closure, writes and resizes return -`io.ErrClosedPipe`. Request EOF is not equivalent to a terminal Ctrl-D keystroke. - -## ExecChunk - -Each chunk from `ExecStream.Next()` carries a segment of process output along with which stream it came from. - -```go -type ExecChunk struct { - Data []byte - Stream StreamType -} -``` - -| Field | Type | Description | -|----------|------------|---------------------------------------------------| -| `Data` | `[]byte` | Raw output bytes from the process. | -| `Stream` | StreamType | Either `StreamStdout` or `StreamStderr`. | - -See also: [Error Handling](../error-handling.md), [Testing](../testing.md) diff --git a/sdk/go/docs/src/api/fake.md b/sdk/go/docs/src/api/fake.md deleted file mode 100644 index ddd8a57fa5..0000000000 --- a/sdk/go/docs/src/api/fake.md +++ /dev/null @@ -1,122 +0,0 @@ -# Fake - -Package: `openshell/v1/fake` - -The fake package provides an in-memory fake implementation of all SDK -client interfaces for use in consumer test suites. It follows the -`client-go/kubernetes/fake` pattern: in-memory stores, watch event -broadcasting, and matching `StatusError` codes for equivalent error -conditions (`NotFound`, `AlreadyExists`, `Unavailable`, `Unimplemented`). - -## Quick Start - -```go -import "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/fake" - -func TestSandboxLifecycle(t *testing.T) { - client := fake.NewClient() - defer client.Close() - - ctx := context.Background() - - sb, err := client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}) - require.NoError(t, err) - assert.Equal(t, types.SandboxProvisioning, sb.Status.Phase) - - sb, err = client.Sandboxes().WaitReady(ctx, "default", "my-sandbox") - require.NoError(t, err) - assert.Equal(t, types.SandboxReady, sb.Status.Phase) - - deletion, err := client.Sandboxes().Delete(ctx, "default", "my-sandbox") - require.NoError(t, err) - assert.Equal(t, v1.DeletionCompleted, deletion.Outcome) -} -``` - -## Creating a Client - -```go -func NewClient(opts ...ClientOption) *Client -``` - -Returns a fake client implementing `v1.ClientInterface` with all -sub-clients wired up. Default health result is healthy. Use options -to customize initial state: - -```go -client := fake.NewClient( - fake.WithHealthResult(&types.HealthResult{Healthy: false}), - fake.WithCurrentUser(&types.CurrentUser{Subject: "test-user"}), - fake.WithGatewayInfo(&types.GatewayInfo{Version: "1.0.0"}), -) -``` - -## Pre-populating State - -Seed objects directly into the fake stores for test setup: - -```go -client := fake.NewClient() - -client.AddSandbox("default", &types.Sandbox{ - Name: "pre-existing", - Status: types.SandboxStatus{Phase: types.SandboxReady}, -}) - -client.AddSandboxTemplate("default", &types.SandboxWorkloadTemplate{ - Name: "gpu-kata", - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{Image: "python:3.12"}, - }, -}) - -client.AddProvider("default", &types.Provider{ - Name: "my-provider", - Spec: types.ProviderSpec{Type: "docker"}, -}) - -client.AddWorkspace(&types.Workspace{Name: "staging"}) -client.AddMember("staging", &types.WorkspaceMember{ - PrincipalSubject: "subject-123", - Role: types.WorkspaceRoleAdmin, -}) -``` - -All `Add*` methods deep-copy their arguments; mutating the input after -insertion does not affect the stored object. - -## Sub-Client Coverage - -The fake client implements every stable accessor in `v1.ClientInterface` plus -additive concrete-client accessors such as `SandboxTemplates()`: - -| Accessor | Interface | Behavior | -|----------|-----------|----------| -| `Sandboxes()` | `SandboxInterface` | Full CRUD, Watch, WaitReady | -| `SandboxTemplates()` | `SandboxTemplateInterface` | Full CRUD | -| `Providers()` | `ProviderInterface` | Full CRUD, Ensure | -| `Workspaces()` | `WorkspaceInterface` | Full CRUD, Members | -| `Health()` | `HealthInterface` | Configurable result | -| `Policy()` | `PolicyInterface` | List, GetStatus (draft ops return Unimplemented) | -| `Exec()` | `ExecInterface` | Returns Unimplemented | -| `Files()` | `FileInterface` | Returns Unimplemented | -| `Services()` | `ServiceInterface` | Returns Unimplemented | -| `SSH()` | `SSHInterface` | Input validation, then Unimplemented | -| `TCP()` | `TCPInterface` | Input validation, then Unimplemented | -| `Config()` | `ConfigInterface` | Returns Unimplemented | - -## ClientOption - -| Constructor | Effect | -|-------------|--------| -| `WithHealthResult(r)` | Set the health check return value | -| `WithCurrentUser(u)` | Set the current user return value | -| `WithGatewayInfo(i)` | Set the gateway info return value | - -## Thread Safety - -All operations are safe for concurrent use from multiple goroutines. -`Close` is idempotent and causes all subsequent operations to return -`Unavailable`. - -See also: [Testing Guide](../testing.md) diff --git a/sdk/go/docs/src/api/files.md b/sdk/go/docs/src/api/files.md deleted file mode 100644 index 65026cf6a4..0000000000 --- a/sdk/go/docs/src/api/files.md +++ /dev/null @@ -1,36 +0,0 @@ -# Files - -Accessor: `client.Files()` - -`FileInterface` reserves the upload and download API while keeping sandbox lookup -and SSH-session lifecycle behavior stable. The standalone SDK does not currently -ship an SSH file-transfer transport, so both operations return -`v1.ErrTransportNotAvailable` before performing local validation or gateway RPCs. - -## Upload - -Detect transport availability programmatically: - -```go -err := client.Files().Upload(ctx, "default", "sandbox-123", "./data/config.yaml", "/app/config.yaml") -if errors.Is(err, v1.ErrTransportNotAvailable) { - // Use another transfer mechanism until an SSH transport is available. -} else if err != nil { - log.Fatal(err) -} -``` - -## Download - -`Download` has the same capability gate: - -```go -err := client.Files().Download(ctx, "default", "sandbox-123", "/app/output.log", "./output.log") -if errors.Is(err, v1.ErrTransportNotAvailable) { - // Use another transfer mechanism until an SSH transport is available. -} else if err != nil { - log.Fatal(err) -} -``` - -See also: [Error Handling](../error-handling.md), [Testing](../testing.md) diff --git a/sdk/go/docs/src/api/gateway.md b/sdk/go/docs/src/api/gateway.md deleted file mode 100644 index 66fc9d6f77..0000000000 --- a/sdk/go/docs/src/api/gateway.md +++ /dev/null @@ -1,188 +0,0 @@ -# Gateway - -Package: `openshell/v1/gateway` - -The gateway package reads on-disk gateway configurations created by the -OpenShell Rust CLI and constructs fully wired SDK clients. It eliminates -the boilerplate of locating config files, parsing metadata, loading -tokens, and wiring auth providers. - -## Quick Start - -```go -import "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/gateway" - -// Connect to a named gateway -client, err := gateway.NewClient("prod") -if err != nil { - log.Fatal(err) -} -defer client.Close() -``` - -## Functions - -| Function | Description | -|----------|-------------| -| `NewClient(name, opts...)` | Create a fully wired SDK client from gateway config | -| `LoadConfig(name)` | Parse gateway config without connecting | -| `ListGateways()` | Enumerate all available gateways | - -### NewClient - -```go -func NewClient(name string, opts ...ClientOption) (*v1.Client, error) -``` - -Creates a fully configured SDK client for the named gateway. If `name` -is empty, the active gateway (set via `openshell gateway use`) is used. - -The function resolves the gateway directory, parses `metadata.json`, -loads tokens lazily, maps the auth mode to an SDK auth provider, and -applies any `ClientOption` values before delegating to `v1.NewClient`. - -```go -// Named gateway -client, err := gateway.NewClient("prod") - -// Active gateway -client, err := gateway.NewClient("") - -// With options -client, err := gateway.NewClient("staging", - gateway.WithTimeout(10 * time.Second), - gateway.WithLogger(myLogger), -) -``` - -**Errors**: `ErrGatewayNotFound`, `ErrConfigParse`, -`ErrUnsupportedAuthMode`, `ErrInvalidGatewayName`, `ErrNoActiveGateway` - -### LoadConfig - -```go -func LoadConfig(name string) (*Config, error) -``` - -Parses gateway configuration without creating a client connection. -Returns a frozen snapshot; changes to on-disk files after the call -are not reflected. - -```go -cfg, err := gateway.LoadConfig("staging") -if err != nil { - log.Fatal(err) -} -fmt.Printf("Endpoint: %s, Auth: %s\n", cfg.Endpoint, cfg.AuthMode) -``` - -### ListGateways - -```go -func ListGateways() ([]Info, error) -``` - -Enumerates all available gateways from user and system directories. -User gateways appear first. Duplicate names resolve to user precedence. -Returns an empty slice (not an error) when no gateways are configured. - -```go -gateways, err := gateway.ListGateways() -for _, gw := range gateways { - fmt.Printf("%s (active=%v, source=%s)\n", gw.Name, gw.Active, gw.Source) -} -``` - -## Types - -### Config - -```go -type Config struct { - Name string // Validated gateway name - Endpoint string // Host:port of the gateway - AuthMode AuthMode // Resolved auth mode - Source ConfigSource // User or System origin - Dir string // Absolute path to gateway config directory -} -``` - -### Info - -```go -type Info struct { - Name string // Gateway name from directory listing - Active bool // Whether this is the active gateway - Source ConfigSource // User or System origin -} -``` - -### AuthMode - -| Value | Constant | SDK AuthProvider | -|-------|----------|-----------------| -| `""` or `"none"` | `AuthModeNone` | `v1.NoAuth()` | -| `"plaintext"` | `AuthModePlaintext` | `v1.NoAuth()` + insecure TLS | -| `"cloudflare_jwt"` | `AuthModeCloudflareJWT` | Lazy edge token auth | -| `"oidc"` | `AuthModeOIDC` | `v1.RefreshableToken` that reads `oidc_token.json` near expiry and fails once the token has expired | -| `"mtls"` | `AuthModeMTLS` | Unsupported (use `WithAuth`) | - -### ClientOption - -| Constructor | Effect | -|-------------|--------| -| `WithLogger(l)` | Set logger on the SDK client | -| `WithTimeout(d)` | Set connection timeout | -| `WithTLS(cfg)` | Override TLS settings from gateway config | -| `WithAuth(provider)` | Override auto-resolved auth provider | -| `WithRetryPolicy(p)` | Set retry policy | - -## Error Handling - -All errors support `errors.Is` for classification: - -```go -client, err := gateway.NewClient("my-gateway") -if errors.Is(err, gateway.ErrGatewayNotFound) { - fmt.Println("Gateway not configured. Run: openshell gateway add my-gateway") -} -``` - -Tokens load lazily, so `NewClient` does not return `ErrTokenLoad`. For `oidc` -gateways, a missing, malformed, or expired `oidc_token.json` surfaces on RPCs as -an error for which `v1.IsUnauthenticated(err)` is true, and its message includes -``run `openshell gateway login ` ``. - -| Error | Meaning | -|-------|---------| -| `ErrGatewayNotFound` | No gateway directory in user or system paths | -| `ErrConfigParse` | metadata.json missing or malformed | -| `ErrTokenLoad` | Token file missing, unreadable, malformed, or expired; RPCs report it as Unauthenticated | -| `ErrUnsupportedAuthMode` | Unrecognized auth_mode value | -| `ErrInvalidGatewayName` | Name fails validation | -| `ErrNoActiveGateway` | No active gateway configured | - -## On-Disk Layout - -The package reads gateway metadata from these locations: - -``` -$XDG_CONFIG_HOME/openshell/ (user, default: ~/.config/openshell/) -├── active_gateway # Plain text: active gateway name -└── gateways/ - └── / - ├── metadata.json # {"endpoint":"...","auth_mode":"...","name":"..."} - ├── edge_token # Cloudflare edge JWT (plaintext) - ├── cf_token # Legacy edge token (fallback) - └── oidc_token.json # OIDC token bundle - -/etc/openshell/gateways/ (system, fallback) -``` - -User gateways take precedence over system gateways with the same name. - -## Thread Safety - -All exported functions (`NewClient`, `LoadConfig`, `ListGateways`) are -safe for concurrent use from multiple goroutines. Token loading uses -internal synchronization. diff --git a/sdk/go/docs/src/api/health.md b/sdk/go/docs/src/api/health.md deleted file mode 100644 index 397a2b5d7f..0000000000 --- a/sdk/go/docs/src/api/health.md +++ /dev/null @@ -1,32 +0,0 @@ -# Health - -Accessor: `client.Health()` - -Check the health status of the connected OpenShell gateway. - -## Check - -Perform a health check against the gateway. Returns the overall status and -component-level details. - -```go -result, err := client.Health().Check(ctx) -if err != nil { - log.Fatal(err) -} -fmt.Printf("Gateway healthy: %v\n", result.Healthy) -``` - -This is useful for readiness probes or verifying connectivity before executing -other operations. - -```go -// Quick connectivity check before starting work -if result, err := client.Health().Check(ctx); err != nil { - log.Fatalf("Gateway unreachable: %v", err) -} else if !result.Healthy { - log.Fatal("Gateway is not healthy") -} -``` - -See also: [Error Handling](../error-handling.md) diff --git a/sdk/go/docs/src/api/oidc.md b/sdk/go/docs/src/api/oidc.md deleted file mode 100644 index 1f3c1bef27..0000000000 --- a/sdk/go/docs/src/api/oidc.md +++ /dev/null @@ -1,173 +0,0 @@ -# OIDC Login - -Package: `openshell/v1/oidc` - -The oidc package provides OIDC authentication for OpenShell gateways. -It supports four OAuth2 flows: browser-based authorization code with -PKCE, keyboard fallback, device code (RFC 8628), and client credentials. - -## Quick Start - -```go -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) -} -``` - -## Functions - -| Function | Description | -|----------|-------------| -| `Login(ctx, gatewayName, opts...)` | Interactive login via browser or keyboard flow | -| `DeviceLogin(ctx, opts...)` | Device authorization grant (RFC 8628) | -| `ClientCredentials(ctx, opts...)` | Non-interactive client credentials grant | - -### Login - -```go -func Login(ctx context.Context, gatewayName string, opts ...LoginOption) (*oauth2.Token, error) -``` - -Performs an interactive OIDC login. When `gatewayName` is provided, the -OIDC issuer and client ID are read from the gateway's `metadata.json`. -The default flow opens a browser for authorization code exchange with -PKCE. If the provider does not support PKCE (S256), the flow proceeds -without it. - -Tokens are persisted to the gateway's config directory as -`oidc_token.json`, in the same format as `openshell gateway login`, so CLI -commands against that gateway can use them. Subsequent calls reuse the stored -token only when it is valid and was issued for the gateway's issuer and client -ID; otherwise `Login` runs the flow again and rewrites the file. - -For standalone use (no gateway), pass an empty `gatewayName` with -`WithIssuer` and `WithClientID`. Combine with `WithInMemory` to skip -disk persistence. - -### DeviceLogin - -```go -func DeviceLogin(ctx context.Context, opts ...LoginOption) (*oauth2.Token, error) -``` - -Performs an OAuth2 device authorization grant (RFC 8628). The flow -requests a device code and user code from the provider, displays them -via `WithDisplayFunc` (or stdout), and polls the token endpoint until -the user completes authorization. - -Requires `WithIssuer` and `WithClientID`, or `WithGateway`. - -### ClientCredentials - -```go -func ClientCredentials(ctx context.Context, opts ...LoginOption) (*oauth2.Token, error) -``` - -Performs a non-interactive OAuth2 client credentials grant. Requires -`WithIssuer`, `WithClientID`, and `WithClientSecret` (or `WithGateway` -combined with `WithClientSecret`). The client secret is never included -in error messages. - -### NewClientCredentialsAuth - -```go -func NewClientCredentialsAuth(opts ...LoginOption) (types.AuthProvider, error) -``` - -Returns a lazy renewable auth provider for `v1.Config.Auth` or -`gateway.WithAuth`. It caches access tokens in memory, renews within 30 seconds -of expiry, coalesces concurrent exchanges, and fails closed if renewal fails. -Use `WithClientSecretProvider` when the application rotates secret material. - -## Options - -| Option | Description | -|--------|-------------| -| `WithIssuer(url)` | OIDC provider issuer URL | -| `WithClientID(id)` | OAuth2 client ID | -| `WithClientSecret(secret)` | OAuth2 client secret (for client credentials) | -| `WithClientSecretProvider(provider)` | Resolve the client secret for each exchange | -| `WithAudience(audience)` | Optional OAuth2 resource-server audience | -| `WithScopes(scopes...)` | Custom scopes (client credentials has no implicit scopes) | -| `WithCallbackPort(port)` | Fixed port for localhost callback (default: tries 8000, then 18000) | -| `WithTimeout(d)` | Auth flow timeout (default: 2 minutes) | -| `WithKeyboardFlow()` | Use keyboard flow instead of browser | -| `WithInMemory()` | Skip token persistence to disk | -| `WithDisplayFunc(fn)` | Custom display for device code flow | -| `WithGateway(name)` | Resolve OIDC config from gateway metadata | - -## Error Handling - -The package defines sentinel errors for `errors.Is()` matching: - -| Error | Description | -|-------|-------------| -| `ErrDiscovery` | OIDC discovery document fetch failed | -| `ErrAuthCode` | Authorization code exchange failed | -| `ErrDeviceCode` | Device code flow failed | -| `ErrClientCredentials` | Client credentials exchange failed | -| `ErrTimeout` | Authentication flow timed out | -| `ErrCallbackServer` | Localhost callback server failed | -| `ErrTokenPersist` | Token read/write failed | -| `ErrOIDCConfig` | Missing or invalid OIDC configuration | - -```go -token, err := oidc.Login(ctx, "my-gateway") -if errors.Is(err, oidc.ErrDiscovery) { - // Provider unreachable -} -if errors.Is(err, oidc.ErrTimeout) { - // User did not complete login in time -} -``` - -## Gateway Integration - -When a gateway's `metadata.json` contains `oidc_issuer` and -`oidc_client_id` fields, `Login` and `DeviceLogin` can resolve -configuration automatically: - -```go -// Login reads OIDC config from the gateway -token, err := oidc.Login(ctx, "my-gateway") - -// Then use the token with the SDK client -client, err := v1.NewClient(v1.Config{ - Address: "gateway.example.com:443", - Auth: v1.StaticToken(token.AccessToken), -}) -``` - -## Flows - -### Browser Flow (default) - -1. Discovers OIDC endpoints from issuer -2. Generates PKCE code verifier and S256 challenge -3. Opens browser to authorization URL -4. Starts localhost callback server to receive the auth code -5. Exchanges code for tokens with PKCE verification -6. Persists tokens to disk - -### Keyboard Flow - -Same as browser flow, but prints the authorization URL for the user to -copy manually. The user pastes the authorization code back into the -terminal. Use `WithKeyboardFlow()` to enable. - -### Device Code Flow (RFC 8628) - -1. Requests a device code and user code from the provider -2. Displays the verification URL and user code -3. Polls the token endpoint at the provider's requested interval -4. Handles `slow_down` responses by increasing the poll interval - -### Client Credentials Flow - -1. Discovers OIDC endpoints from issuer -2. Exchanges client ID and secret for an access token -3. No user interaction required diff --git a/sdk/go/docs/src/api/overview.md b/sdk/go/docs/src/api/overview.md deleted file mode 100644 index 0630c1057a..0000000000 --- a/sdk/go/docs/src/api/overview.md +++ /dev/null @@ -1,65 +0,0 @@ -# API Overview - -The OpenShell Go SDK exposes typed interfaces through the sub-client pattern. -You access each interface through a typed accessor on the concrete `Client`. - -## Interface Summary - -### Top-Level Interfaces - -| Interface | Accessor | Description | -|-----------|----------|-------------| -| [SandboxInterface](sandboxes.md) | `client.Sandboxes()` | Create, manage, and watch sandbox lifecycle | -| [SandboxTemplateInterface](sandbox-templates.md) | `client.SandboxTemplates()` | Manage reusable sandbox templates | -| [ExecInterface](exec.md) | `client.Exec()` | Run commands, stream output, interactive sessions | -| [ProviderInterface](providers.md) | `client.Providers()` | Manage compute providers and their lifecycle | -| [ServiceInterface](services.md) | `client.Services()` | Expose and manage HTTP services inside sandboxes | -| [FileInterface](files.md) | `client.Files()` | Upload and download files to/from sandboxes | -| [HealthInterface](health.md) | `client.Health()` | Check gateway health status | -| [SSHInterface](ssh.md) | `client.SSH()` | Create SSH sessions and tunnels to sandboxes | -| [TCPInterface](tcp.md) | `client.TCP()` | Forward TCP connections to sandbox ports | -| [ConfigInterface](config.md) | `client.Config()` | Read and update sandbox and gateway configuration | -| [PolicyInterface](policy.md) | `client.Policy()` | Manage draft policy recommendations | - -### Convenience Packages - -| Package | Entry Point | Description | -|---------|-------------|-------------| -| [gateway](gateway.md) | `gateway.NewClient(name)` | Read CLI gateway configs and auto-wire clients | - -### Provider Sub-Interfaces - -These are accessed through `client.Providers()`: - -| Interface | Accessor | Description | -|-----------|----------|-------------| -| [ProfileInterface](profiles.md) | `client.Providers().Profiles()` | Manage provider type profiles | -| [RefreshInterface](refresh.md) | `client.Providers().Refresh()` | Configure credential refresh strategies | - -## Interfaces - -Each interface has a reference page with method signatures and usage examples: - -- **[Sandboxes](sandboxes.md)**: Create sandboxes, wait for readiness, watch state changes, manage providers, retrieve logs. -- **[Sandbox Templates](sandbox-templates.md)**: Create reusable workload templates and launch sandboxes from them. -- **[Exec](exec.md)**: Execute commands with one-shot, streaming, or interactive modes. -- **[Providers](providers.md)**: Register and manage compute providers. Includes sub-clients for profiles and credential refresh. -- **[Services](services.md)**: Expose and manage HTTP services inside sandboxes. -- **[Files](files.md)**: Upload and download files to/from sandboxes. -- **[Health](health.md)**: Check gateway health status. -- **[SSH](ssh.md)**: Create SSH sessions and tunnels to sandboxes. -- **[TCP](tcp.md)**: Forward TCP connections to sandbox ports. -- **[Config](config.md)**: Read and update sandbox and gateway configuration. -- **[Policy](policy.md)**: Manage draft policy recommendations. -- **[Profiles](profiles.md)**: Manage provider type profiles (via `client.Providers().Profiles()`). -- **[Refresh](refresh.md)**: Configure credential refresh strategies (via `client.Providers().Refresh()`). - -## Common Patterns - -SDK methods follow these conventions: - -- Network operations take `context.Context`; list constructors return a pager, - and each `NextPage` call accepts the request context -- Methods that can fail return `(result, error)` -- List methods accept variadic options and `ListAll` is the explicit exhaustive path -- Errors from the gateway carry a `StatusError` with a typed code (see [Error Handling](../error-handling.md)) diff --git a/sdk/go/docs/src/api/policy.md b/sdk/go/docs/src/api/policy.md deleted file mode 100644 index 3d2573c086..0000000000 --- a/sdk/go/docs/src/api/policy.md +++ /dev/null @@ -1,128 +0,0 @@ -# Policy - -Accessor: `client.Policy()` - -Manage network policies for sandboxes through a draft-based workflow. Policies -go through a draft, review, and approval cycle before being applied. - -## GetDraft - -Retrieve the current draft policy for a sandbox. - -```go -draft, err := client.Policy().GetDraft(ctx, "default", "my-sandbox") -if err != nil { - log.Fatal(err) -} -for _, chunk := range draft.Chunks { - fmt.Printf("Chunk %s: rule=%s, status=%s, confidence=%.1f\n", - chunk.ID, chunk.RuleName, chunk.Status, chunk.Confidence) -} -``` - -## ApproveAllDraftChunks - -Approve all pending chunks in a single operation. - -```go -result, err := client.Policy().ApproveAllDraftChunks(ctx, "default", "my-sandbox") -if err != nil { - log.Fatal(err) -} -fmt.Printf("Approved %d chunks (skipped %d), policy version: %d\n", - result.ChunksApproved, result.ChunksSkipped, result.PolicyVersion) -``` - -## GetStatus - -Check the current policy enforcement status for a sandbox. - -```go -status, err := client.Policy().GetStatus(ctx, "default", "my-sandbox") -if err != nil { - log.Fatal(err) -} -fmt.Printf("Active version: %d, revision status: %s\n", - status.ActiveVersion, status.Revision.Status) -``` - -## List - -`List` returns a lazy pager over policy revisions for one sandbox. Use -`ListAll` to collect every page. - -```go -revisions, err := client.Policy().ListAll(ctx, "default", "my-sandbox") -if err != nil { - log.Fatal(err) -} -for _, rev := range revisions { - fmt.Printf("Version %d: %s (status: %s)\n", - rev.Version, rev.CreatedAt, rev.Status) -} -``` - -## RejectDraftChunk - -Reject a specific draft chunk, providing a reason. - -```go -err := client.Policy().RejectDraftChunk(ctx, "default", "my-sandbox", "chunk-abc", "Too permissive") -if err != nil { - log.Fatal(err) -} -``` - -## EditDraftChunk - -Modify the proposed rule in a draft chunk before approval. - -```go -err := client.Policy().EditDraftChunk(ctx, "default", "my-sandbox", "chunk-abc", &v1.NetworkPolicyRule{ - Name: "allow-api", - Endpoints: []v1.PolicyNetworkEndpoint{{ - Host: "api.example.com", - Port: 443, - Protocol: "tcp", - }}, -}) -if err != nil { - log.Fatal(err) -} -``` - -## Append allow or deny rules - -Use `client.Config().Update` with `AddAllowRules` or `AddDenyRules` to append request matchers to an existing base-policy endpoint. Both operations require an `L7RuleTarget` naming the rule, host, every affected port, and every binary governed by that rule. The gateway rejects missing scope or a scope that differs from the current policy without applying the merge batch. - -```go -result, err := client.Config().Update(ctx, "default", &v1.ConfigUpdate{ - Name: "my-sandbox", - MergeOperations: []v1.PolicyMergeOperation{{ - AddAllowRules: &v1.AddAllowRules{ - Target: &v1.L7RuleTarget{ - RuleName: "api", - Host: "api.example.com", - Ports: []uint32{443, 8443}, - Binaries: []v1.PolicyNetworkBinary{ - {Path: "/usr/bin/curl"}, - {Path: "/usr/bin/wget"}, - }, - }, - Rules: []v1.L7Rule{{ - Allow: &v1.L7Allow{Method: "POST", Path: "/admin"}, - }}, - }, - }}, -}) -if err != nil { - log.Fatal(err) -} -fmt.Printf("Policy version: %d\n", result.Version) -``` - -This example explicitly permits both binaries to make the new request on both ports. For a rule that permits any binary, set `AnyBinary: true` and omit `Binaries`. Omitting both fields is invalid; the SDK never infers any-binary permission. - -`Target.Path` selects the existing endpoint path and is separate from the appended request matcher's path. Leave it `nil` when the rule, host, and ports identify a unique endpoint. To choose an endpoint with a specific path, pass a pointer to that path; a pointer to an empty string selects an endpoint without a path selector. Use `AddDenyRules` with the same target shape and a `DenyRules` payload to append deny matchers. - -See also: [Error Handling](../error-handling.md), [Testing](../testing.md) diff --git a/sdk/go/docs/src/api/profiles.md b/sdk/go/docs/src/api/profiles.md deleted file mode 100644 index 57b3b949c6..0000000000 --- a/sdk/go/docs/src/api/profiles.md +++ /dev/null @@ -1,86 +0,0 @@ -# Profiles - -Accessor: `client.Providers().Profiles()` - -Manage provider profiles for AI model providers. Profiles define connection details, -credentials, and model mappings for providers like OpenAI, Anthropic, or custom endpoints. - -## List - -`List` returns a lazy pager over visible provider profiles. Use `ListAll` to -collect every page. - -```go -profiles, err := client.Providers().Profiles().ListAll(ctx, "default") -if err != nil { - log.Fatal(err) -} -for _, p := range profiles { - fmt.Printf("Profile: %s (%s)\n", p.ID, p.DisplayName) -} -``` - -## Import - -Import one or more provider profiles from configuration items. - -```go -result, err := client.Providers().Profiles().Import(ctx, "default", []v1.ProfileImportItem{ - { - Profile: v1.ProviderProfile{ - DisplayName: "OpenAI", - Category: v1.ProfileCategoryInference, - }, - Source: "manual", - }, -}) -if err != nil { - log.Fatal(err) -} -fmt.Printf("Imported: %v\n", result.Imported) -``` - -## Update - -Update a profile using optimistic concurrency control via the resource version. - -```go -profile, err := client.Providers().Profiles().Get(ctx, "default", "profile-id") -if err != nil { - log.Fatal(err) -} - -profile.DisplayName = "Updated Provider" -result, err := client.Providers().Profiles().Update( - ctx, "default", - profile.ID, - profile.ResourceVersion, - v1.ProfileImportItem{Profile: *profile, Source: "manual"}, -) -if err != nil { - // See [Error Handling](../error-handling.md) for conflict errors - log.Fatal(err) -} -``` - -## Lint - -Validate profile configurations without persisting them. Useful for pre-flight checks. - -```go -items := []v1.ProfileImportItem{ - { - Profile: v1.ProviderProfile{DisplayName: "Test"}, - Source: "manual", - }, -} -result, err := client.Providers().Profiles().Lint(ctx, "default", items) -if err != nil { - log.Fatal(err) -} -for _, d := range result.Diagnostics { - fmt.Printf("[%s] %s: %s\n", d.Severity, d.Field, d.Message) -} -``` - -See also: [Error Handling](../error-handling.md), [Testing](../testing.md) diff --git a/sdk/go/docs/src/api/providers.md b/sdk/go/docs/src/api/providers.md deleted file mode 100644 index 5ce6b98551..0000000000 --- a/sdk/go/docs/src/api/providers.md +++ /dev/null @@ -1,161 +0,0 @@ -# Providers - -Accessor: `client.Providers()` - -Register and manage compute providers (AI inference endpoints). Exposes -sub-clients for [Profiles](profiles.md) and [Refresh](refresh.md). - -## Create - -Register a new provider with the gateway. - -```go -provider, err := client.Providers().Create(ctx, "default", &v1.Provider{ - Name: "my-openai", - Type: "openai", - Spec: v1.ProviderSpec{ - Credentials: map[string]string{ - "api_key": "sk-...", - }, - Config: map[string]string{ - "base_url": "https://api.openai.com/v1", - }, - }, -}) -if err != nil { - log.Fatal(err) -} -fmt.Println("Created provider:", provider.Name) -``` - -## Get - -Fetch a provider by name. - -```go -provider, err := client.Providers().Get(ctx, "default", "my-openai") -if err != nil { - log.Fatal(err) -} -fmt.Println("Provider type:", provider.Type) -``` - -## List - -`List` returns a lazy pager over registered providers. `ListAll` follows every -continuation token; `PageSize` controls each request. - -```go -// List all providers -providers, err := client.Providers().ListAll(ctx, "default") -if err != nil { - log.Fatal(err) -} -for _, p := range providers { - fmt.Println(p.Name, p.Type) -} - -// With a smaller page size -providers, err = client.Providers().ListAll(ctx, "default", v1.ListOptions{ - PageSize: 10, -}) - -// Platform Admin only: list across all workspaces -allProviders, err := client.Providers().ListAll(ctx, "", v1.ListOptions{ - AllWorkspaces: true, -}) -``` - -## Update - -Update an existing provider's configuration or credentials. - -```go -provider, err := client.Providers().Get(ctx, "default", "my-openai") -if err != nil { - log.Fatal(err) -} - -provider.Spec.Credentials["api_key"] = "sk-new-key" -updated, err := client.Providers().Update(ctx, "default", provider) -if err != nil { - log.Fatal(err) -} -fmt.Println("Updated provider:", updated.Name) -``` - -## Delete - -Remove a provider by name. - -```go -deletion, err := client.Providers().Delete(ctx, "default", "my-openai") -if err != nil { - log.Fatal(err) -} -``` - -## Ensure - -Create or update a provider in a single idempotent call. If a provider with the given name exists, it is updated; otherwise a new one is created. This is the recommended way to register providers because it avoids "already exists" errors when re-registering. - -```go -provider, err := client.Providers().Ensure(ctx, "default", &v1.Provider{ - Name: "my-openai", - Type: "openai", - Spec: v1.ProviderSpec{ - Credentials: map[string]string{ - "api_key": "sk-...", - }, - }, -}) -if err != nil { - log.Fatal(err) -} -fmt.Println("Provider ready:", provider.Name) -``` - -Ensure is an SDK-level convenience. It calls `GetProvider` first, then either `CreateProvider` or `UpdateProvider` depending on whether the provider already exists. - -## Sub-Clients - -The `ProviderInterface` exposes two sub-client accessors for related operations. These are pure client-side accessors with no corresponding gRPC call. - -### Profiles - -`client.Providers().Profiles()` returns a [ProfileInterface](profiles.md) for managing provider type profiles. Profiles define templates and defaults for different provider types. - -### Refresh - -`client.Providers().Refresh()` returns a [RefreshInterface](refresh.md) for configuring credential refresh strategies. Use it to set up automatic credential rotation for providers with expiring credentials. - -## Provider - -The `Provider` type represents a registered compute provider. - -| Field | Type | Description | -|-------------------|------------------------|--------------------------------------------| -| `ID` | string | Server-assigned unique identifier | -| `Name` | string | User-chosen name (unique per gateway) | -| `Type` | string | Provider type (e.g., `"openai"`, `"azure"`) | -| `CreatedAt` | time.Time | Timestamp of creation | -| `Labels` | map[string]string | Key-value metadata labels | -| `ResourceVersion` | uint64 | Optimistic concurrency version | -| `Spec` | ProviderSpec | Configuration and credentials | - -## ProviderSpec - -`ProviderSpec` holds provider-specific configuration and credentials. - -| Field | Type | Description | -|------------------------|---------------------------|-------------------------------------------------| -| `Credentials` | map[string]string | Authentication credentials (e.g., API keys) | -| `Config` | map[string]string | Provider-specific configuration values | -| `CredentialExpiresAt` | map[string]time.Time | Expiration timestamps for credentials | - -The curated Go API uses a zero `time.Time` value to clear an expiration during -an update. Consequently, it cannot represent the protobuf minimum timestamp -(`0001-01-01T00:00:00Z`) in this map. Use the raw protobuf API if that exact -timestamp is required. Other pointer-based timestamp fields preserve it. - -See also: [Profiles](profiles.md), [Refresh](refresh.md), [Error Handling](../error-handling.md), [Testing](../testing.md) diff --git a/sdk/go/docs/src/api/refresh.md b/sdk/go/docs/src/api/refresh.md deleted file mode 100644 index d738ae059e..0000000000 --- a/sdk/go/docs/src/api/refresh.md +++ /dev/null @@ -1,57 +0,0 @@ -# Refresh - -Accessor: `client.Providers().Refresh()` - -Manage credential refresh schedules for provider profiles. Configure automatic -rotation of API keys and monitor refresh status. - -## GetStatus - -Check the refresh status for a specific provider credential. - -```go -statuses, err := client.Providers().Refresh().GetStatus(ctx, "default", "openai", "default") -if err != nil { - log.Fatal(err) -} -for _, s := range statuses { - fmt.Printf("Key: %s, Last refresh: %s, Next: %s\n", - s.CredentialKey, s.LastRefreshAt, s.NextRefreshAt) -} -``` - -## Configure - -Set up automatic credential refresh with a defined strategy and material. - -```go -status, err := client.Providers().Refresh().Configure(ctx, "default", &v1.RefreshConfig{ - Provider: "openai", - CredentialKey: "default", - Strategy: v1.RefreshStrategyOAuth2ClientCredentials, - Material: map[string]string{ - "client_id": "my-client-id", - "client_secret": "my-client-secret", - "token_url": "https://oauth.example.com/token", - }, - SecretMaterialKeys: []string{"client_secret"}, -}) -if err != nil { - log.Fatal(err) -} -fmt.Printf("Refresh configured, next rotation: %s\n", status.NextRefreshAt) -``` - -## Rotate - -Manually trigger an immediate credential rotation. - -```go -status, err := client.Providers().Refresh().Rotate(ctx, "default", "openai", "default") -if err != nil { - log.Fatal(err) -} -fmt.Printf("Rotated successfully at %s\n", status.LastRefreshAt) -``` - -See also: [Error Handling](../error-handling.md), [Profiles](profiles.md) diff --git a/sdk/go/docs/src/api/sandbox-templates.md b/sdk/go/docs/src/api/sandbox-templates.md deleted file mode 100644 index c57855b9cc..0000000000 --- a/sdk/go/docs/src/api/sandbox-templates.md +++ /dev/null @@ -1,127 +0,0 @@ -# Sandbox Templates - -Accessor: `client.SandboxTemplates()` - -Manage reusable, workspace-scoped sandbox workload templates. Templates own the -portable workload shape and optional driver-specific config. Sandbox creation -can reference a template by name while supplying only governance fields such as -providers or policy. - -The Go SDK names the reusable resource `SandboxWorkloadTemplate` because -`SandboxTemplate` already represents the legacy inline compute template inside -`SandboxSpec`. - -## Create - -Creates a reusable template in a workspace. - -```go -gpuCount := uint32(1) - -template, err := client.SandboxTemplates().Create(ctx, "default", &v1.SandboxWorkloadTemplate{ - Name: "gpu-kata", - Labels: map[string]string{ - "team": "platform", - }, - Spec: v1.SandboxWorkloadTemplateSpec{ - Workload: &v1.SandboxWorkloadConfig{ - Image: "nvcr.io/nvidia/openshell:latest", - Environment: map[string]string{ - "NVIDIA_VISIBLE_DEVICES": "all", - }, - Resources: &v1.SandboxResources{ - CPU: "2", - Memory: "8Gi", - GPU: &v1.SandboxGPURequirements{Count: &gpuCount}, - }, - }, - DriverConfig: map[string]any{ - "kubernetes": map[string]any{ - "pod": map[string]any{ - "runtime_class_name": "kata", - }, - }, - }, - DesiredServiceLevel: &v1.SandboxServiceLevel{ - Startup: &v1.SandboxStartup{ - ReadyWithin: 45 * time.Second, - MaxBurst: 2, - }, - }, - }, -}) -``` - -Set `GPU: &v1.SandboxGPURequirements{}` to request the active driver's default -GPU assignment without specifying a count. - -## Create a Sandbox From a Template - -Use `CreateSandboxFromTemplate` to create a sandbox from a named reusable -template without changing the legacy `Sandboxes()` interface. - -```go -sb, err := client.CreateSandboxFromTemplate( - ctx, - "default", - "training-run", - "gpu-kata", - &v1.SandboxSpec{ - Providers: []string{"openai"}, - Policy: policy, - }, - v1.WithLabels(map[string]string{"job": "training"}), -) -``` - -When creating from a template, the spec should only include governance fields: -`Providers`, `Policy`, `Command`, and `TTY`. Workload fields such as image, -environment, CPU, memory, GPU, and driver config come from the template. - -## Get - -Retrieves a template by name. - -```go -template, err := client.SandboxTemplates().Get(ctx, "default", "gpu-kata") -fmt.Println(template.Spec.Workload.Image) -``` - -## List - -`List` returns a lazy pager over matching templates in one workspace or across -all workspaces. Use `ListAll` to follow every continuation token automatically. - -```go -templates, err := client.SandboxTemplates().ListAll(ctx, "default", v1.ListOptions{ - PageSize: 50, -}) - -allTemplates, err := client.SandboxTemplates().ListAll(ctx, "", v1.ListOptions{ - AllWorkspaces: true, -}) -``` - -## Delete - -Deletes a template by name. Existing sandboxes created from the template are not -deleted. - -```go -deletion, err := client.SandboxTemplates().Delete(ctx, "default", "gpu-kata") -``` - -## Fake Client - -The fake client includes the same template sub-client and can be pre-populated -for tests. - -```go -client := fake.NewClient() -client.AddSandboxTemplate("default", &types.SandboxWorkloadTemplate{ - Name: "gpu-kata", - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{Image: "python:3.12"}, - }, -}) -``` diff --git a/sdk/go/docs/src/api/sandboxes.md b/sdk/go/docs/src/api/sandboxes.md deleted file mode 100644 index 58c46d2fee..0000000000 --- a/sdk/go/docs/src/api/sandboxes.md +++ /dev/null @@ -1,304 +0,0 @@ -# Sandboxes - -Accessor: `client.Sandboxes()` - -Manage sandbox lifecycle: create, inspect, delete, attach/detach providers, -wait for readiness, watch state changes, and retrieve logs. - -## Create - -Creates a new sandbox with the given name and spec. - -```go -sb, err := client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{ - Template: &v1.SandboxTemplate{ - Image: "nvcr.io/nvidia/openshell:latest", - }, - Providers: []string{"openai"}, -}, v1.WithLabels(map[string]string{ - "team": "platform", -}), v1.WithServiceExposures(v1.ServiceExposure{TargetPort: 8080})) -fmt.Println(sb.ServiceURLs[""]) -``` - -Create-time service exposures register loopback HTTP endpoints with the -sandbox. Leave `Service` empty for the unnamed endpoint or set it to create a -named endpoint. Routing begins when the sandbox is ready. -`ServiceURLs` returns the routed URLs keyed by service name; the empty key is -the unnamed endpoint. - -Set `GPU: true` to request the active driver's default GPU assignment. Set -`GPUCount` when the sandbox needs a specific GPU count; a non-nil `GPUCount` -also implies `GPU`. - -## Create From Template - -Creates a new sandbox from a reusable sandbox workload template. The template -provides workload fields such as image, environment, resources, and driver -config. The create request supplies governance fields such as providers, -policy, command, and TTY. - -```go -sb, err := client.CreateSandboxFromTemplate(ctx, - "default", - "my-sandbox", - "gpu-kata", - &v1.SandboxSpec{ - Providers: []string{"openai"}, - Policy: policy, - }, - v1.WithLabels(map[string]string{"team": "platform"}), -) -``` - -See [Sandbox Templates](sandbox-templates.md) for template CRUD. - -## Get - -Retrieves a sandbox by name. - -```go -sb, err := client.Sandboxes().Get(ctx, "default", "my-sandbox") -fmt.Println(sb.Status.Phase) // "Ready", "Provisioning", etc. -``` - -### Tool server connections - -`sb.Status.EndpointStatuses` shows each configured tool server endpoint and its last accepted network result in one record. A sandbox can be `Ready` while a tool server connection fails; endpoint results do not change lifecycle readiness. OpenShell currently observes endpoints configured for MCP over HTTP. - -```go -for _, endpoint := range sb.Status.EndpointStatuses { - fmt.Printf("%s:%v%s: %s, reported %s\n", - endpoint.Host, endpoint.Ports, endpoint.Path, - endpoint.LastResult, endpoint.LastReportedAt) -} -``` - -`LastResult` is a typed `v1.EndpointResult`. For example, `v1.EndpointTransportFailed` means the transport failed before an HTTP response arrived. `v1.EndpointHTTPResponseReceived` means the server returned an HTTP status below 400; its body can still contain a tool error. Neither result establishes current availability. - -OpenShell observes real traffic passively and does not expire idle observations. Keep `LastReportedAt` visible when displaying a result: it records when the gateway accepted the observation, not when the request happened. Retained evidence can be accepted after a reset. `v1.EndpointNoObservedExchange` has an empty timestamp and retains the configured address, so the endpoint remains identifiable before traffic is observed or after evidence is invalidated. - -`LastFailure` explains a `v1.EndpointCredentialUnavailable` result when the supervisor classified the cause, and is `nil` otherwise. Each later observation replaces or clears it. `Code` and `Stage` come from closed sets, `Provider` and `CredentialKey` name the configured credential, and no field carries remote text. `RequestID` matches the `x-openshell-request-id` header the workload received, so you can correlate the refused request. - -```go -for _, endpoint := range sb.Status.EndpointStatuses { - if f := endpoint.LastFailure; f != nil && f.Domain == v1.EndpointFailureDomainTokenGrant { - fmt.Printf("%s/%s: %s at %s (request %s)\n", - f.Provider, f.CredentialKey, f.Code, f.Stage, f.RequestID) - if f.RecoveryAction == v1.RefreshRecoveryActionReauthorize { - // Ask the user to replace the provider's OAuth grant. - } - } -} -``` - -### Token grant failures - -`sb.Status.LastTokenGrantFailure` gives the latest classified grant refusal for any destination, including REST requests without an MCP inventory entry. It carries `DestinationHost`, `DestinationPort`, and the same bounded `EndpointFailure` as endpoint status. - -```go -if observation := sb.Status.LastTokenGrantFailure; observation != nil && observation.Failure != nil { - f := observation.Failure - fmt.Printf("%s (port %d): %s, recovery %s, request %s\n", - observation.DestinationHost, observation.DestinationPort, - f.Code, f.RecoveryAction, f.RequestID) -} -``` - -Check `RequestID` against the refused response before acting: a newer failure can replace this record. A successful grant for the same destination, provider, and credential clears it; a failure before credential selection clears on the next successful grant to that destination. Policy, provider, and supervisor-session resets also clear it. `nil` means no retained cause. This is one latest observation, not a request history or a server health check. - -## List - -`List` constructs a lazy pager; `NextPage` fetches one page at a time. -`PageSize` controls each request, and `ListAll` explicitly exhausts the pager. - -```go -// List all sandboxes -sandboxes, err := client.Sandboxes().ListAll(ctx, "default") - -// Process one page at a time -pages, err := client.Sandboxes().List("default", v1.ListOptions{PageSize: 10}) -page, err := pages.NextPage(ctx) - -// With a page size and label filtering -sandboxes, err := client.Sandboxes().ListAll(ctx, "default", v1.ListOptions{ - PageSize: 10, - LabelSelector: "team=platform", -}) - -// Platform Admin only: list across all workspaces -allSandboxes, err := client.Sandboxes().ListAll(ctx, "", v1.ListOptions{ - AllWorkspaces: true, -}) -``` - -## Delete - -Deletes a sandbox by name. - -```go -deletion, err := client.Sandboxes().Delete(ctx, "default", "my-sandbox", v1.DeleteOptions{AllowMissing: true}) -``` - -Missing targets return `NotFound` unless `AllowMissing` is true. Inspect -`deletion.Outcome`: `DeletionAccepted` means cleanup is pending, while -`DeletionCompleted` and `DeletionAlreadyAbsent` establish logical completion. -Unknown values do not establish completion. `deletion.SandboxID` identifies the -original sandbox; do not confuse a same-name replacement with that target. -Allowing absence does not make a retry safe if names can be reused. - -## AttachProvider - -Attaches a provider to a sandbox. The `expectedResourceVersion` enables optimistic concurrency control: pass the sandbox's current `ResourceVersion` to ensure no other client has modified it since your last read. - -```go -sb, _ := client.Sandboxes().Get(ctx, "default", "my-sandbox") - -result, err := client.Sandboxes().AttachProvider(ctx, - "default", "my-sandbox", - "openai", - sb.ResourceVersion, -) -fmt.Println(result.Attached) // true if newly attached -``` - -## DetachProvider - -Detaches a provider from a sandbox. Uses the same optimistic concurrency pattern as `AttachProvider`. - -```go -sb, _ := client.Sandboxes().Get(ctx, "default", "my-sandbox") - -result, err := client.Sandboxes().DetachProvider(ctx, - "default", "my-sandbox", - "openai", - sb.ResourceVersion, -) -fmt.Println(result.Detached) // true if actually detached -``` - -## ListProviders - -Lists all providers currently attached to a sandbox. - -```go -providers, err := client.Sandboxes().ListProviders(ctx, "default", "my-sandbox") -for _, p := range providers { - fmt.Printf("provider: %s (type: %s)\n", p.Name, p.Type) -} -``` - -## WaitReady - -Blocks until the sandbox reaches the `Ready` phase, returning the final sandbox state. Under the hood, WaitReady polls via `Get` at a configurable interval (default 500ms). Use context cancellation or deadlines to set a timeout. - -If the sandbox enters the `Error` phase, WaitReady returns immediately with a `StatusError`. - -```go -// Wait with a 30-second timeout -ctx, cancel := context.WithTimeout(ctx, 30*time.Second) -defer cancel() - -sb, err := client.Sandboxes().WaitReady(ctx, "default", "my-sandbox") -if err != nil { - log.Fatal(err) -} -fmt.Println(sb.Status.Phase) // "Ready" - -// Custom poll interval -sb, err := client.Sandboxes().WaitReady(ctx, "default", "my-sandbox", v1.WaitOptions{ - PollInterval: 2 * time.Second, -}) -``` - -There is no dedicated WaitReady RPC. The SDK implements this by polling `GetSandbox` until the sandbox phase is `Ready` or `Error`. - -## Watch - -Opens a server-streaming connection to observe sandbox state changes in real time. Returns a `WatchInterface[*Sandbox]` that delivers events through a channel. - -The `WatchInterface[T]` provides: - -- `ResultChan() <-chan Event[T]` returns the channel of events -- `Stop()` closes the stream and the channel - -Each `Event[T]` carries: - -- `Type`: one of `EventAdded`, `EventModified`, `EventDeleted`, or `EventError` -- `Object`: the `*Sandbox` at that point in time (`nil` for `EventError`) - -```go -watcher, err := client.Sandboxes().Watch(ctx, "default", "my-sandbox") -if err != nil { - log.Fatal(err) -} -defer watcher.Stop() - -for event := range watcher.ResultChan() { - switch event.Type { - case v1.EventModified: - fmt.Printf("phase: %s\n", event.Object.Status.Phase) - case v1.EventDeleted: - fmt.Println("sandbox deleted") - return - case v1.EventError: - fmt.Println("watch error") - return - } -} -``` - -Setting `StopOnTerminal: true` causes the watcher to close automatically once the sandbox reaches a terminal phase (`Ready` or `Error`). This is useful for provisioning flows where you only care about the outcome. - -```go -watcher, err := client.Sandboxes().Watch(ctx, "default", "my-sandbox", v1.WatchOptions{ - StopOnTerminal: true, -}) -if err != nil { - log.Fatal(err) -} - -for event := range watcher.ResultChan() { - fmt.Printf("phase: %s\n", event.Object.Status.Phase) -} -// Channel closes after Ready or Error -``` - -## GetLogs - -Retrieves log entries from a sandbox. The sandbox is looked up by name (the SDK resolves the name to an internal ID automatically). Use functional options to filter results. - -**Available options:** - -| Option | Description | -|--------|-------------| -| `WithLogLines(n uint32)` | Maximum number of log lines to return | -| `WithLogSince(t time.Time)` | Only include entries at or after this time | -| `WithLogSources(sources ...string)` | Filter by source (e.g., `"gateway"`, `"sandbox"`) | -| `WithLogMinLevel(level string)` | Minimum log level (e.g., `"WARN"`, `"ERROR"`) | - -```go -// Get the last 50 log lines -result, err := client.Sandboxes().GetLogs(ctx, "default", "my-sandbox", - v1.WithLogLines(50), -) -for _, line := range result.Lines { - fmt.Printf("[%s] %s: %s\n", line.Level, line.Source, line.Message) -} - -// Filter by source and level since a specific time -result, err := client.Sandboxes().GetLogs(ctx, "default", "my-sandbox", - v1.WithLogSources("gateway"), - v1.WithLogMinLevel("WARN"), - v1.WithLogSince(time.Now().Add(-1*time.Hour)), -) -``` - -The `LogResult` contains: - -- `Lines []LogLine`: log entries in chronological order -- `BufferTotal uint32`: total number of lines available in the server's buffer - -Each `LogLine` has `Timestamp`, `Level`, `Target`, `Message`, `Source`, and `Fields` (structured key-value data). - -See also: [Error Handling](../error-handling.md), [Testing](../testing.md) diff --git a/sdk/go/docs/src/api/services.md b/sdk/go/docs/src/api/services.md deleted file mode 100644 index 18a753578b..0000000000 --- a/sdk/go/docs/src/api/services.md +++ /dev/null @@ -1,53 +0,0 @@ -# Services - -Accessor: `client.Services()` - -Expose, inspect, and manage network services attached to sandboxes. Services provide -external access to ports running inside a sandbox via managed endpoints. - -## Expose - -Expose a port from a sandbox as a named service endpoint. Set `domain` to `true` -to assign a DNS-routable domain name to the service. - -```go -endpoint, err := client.Services().Expose(ctx, "default", "my-sandbox", "web", 8080, true) -if err != nil { - log.Fatal(err) -} -fmt.Printf("Service available at: %s\n", endpoint.URL) -``` - -## List - -`List` returns a lazy pager over exposed services. Use `ListAll` to collect -every page. - -```go -services, err := client.Services().ListAll(ctx, "default", "my-sandbox") -if err != nil { - log.Fatal(err) -} -for _, svc := range services { - fmt.Printf(" %s -> port %d (%s)\n", svc.Name, svc.TargetPort, svc.URL) -} - -// Platform Admin only: list services across all workspaces -allServices, err := client.Services().ListAll(ctx, "", "", v1.ListOptions{ - AllWorkspaces: true, -}) -``` - -## Delete - -Remove an exposed service. The underlying sandbox port remains accessible -internally but is no longer reachable through the service endpoint. - -```go -deletion, err := client.Services().Delete(ctx, "default", "my-sandbox", "web") -if err != nil { - log.Fatal(err) -} -``` - -See also: [Error Handling](../error-handling.md), [Testing](../testing.md) diff --git a/sdk/go/docs/src/api/ssh.md b/sdk/go/docs/src/api/ssh.md deleted file mode 100644 index dfe03cfbb5..0000000000 --- a/sdk/go/docs/src/api/ssh.md +++ /dev/null @@ -1,55 +0,0 @@ -# SSH - -Accessor: `client.SSH()` - -Create and manage SSH sessions for sandboxes. Supports direct SSH access and -TCP tunneling through SSH connections. - -## CreateSession - -Create a new SSH session for a sandbox. Returns connection details including -host, port, and authentication credentials. - -```go -session, err := client.SSH().CreateSession(ctx, "default", "sandbox-123") -if err != nil { - log.Fatal(err) -} -fmt.Printf("SSH via %s://%s:%d\n", session.GatewayScheme, session.GatewayHost, session.GatewayPort) -``` - -## RevokeSession - -Revoke an active SSH session, immediately terminating any connections using it. - -```go -deletion, err := client.SSH().RevokeSession(ctx, "default", session.Token) -if err != nil { - log.Fatal(err) -} -if deletion.Outcome == v1.DeletionCompleted { - fmt.Println("Session revoked") -} -``` - -## Tunnel - -Create an SSH tunnel that provides a bidirectional stream to a port inside a -sandbox. This combines SSH session creation with TCP forwarding into a single -operation, returning an `io.ReadWriteCloser` for the tunnel. - -```go -tunnel, err := client.SSH().Tunnel(ctx, "default", "my-sandbox", 8080) -if err != nil { - log.Fatal(err) -} -defer tunnel.Close() - -// Use the tunnel as a regular io.ReadWriteCloser -_, err = tunnel.Write([]byte("GET / HTTP/1.0\r\n\r\n")) -if err != nil { - log.Fatal(err) -} -``` - -See also: [TCP Forwarding](tcp.md), [Error Handling](../error-handling.md) diff --git a/sdk/go/docs/src/api/tcp.md b/sdk/go/docs/src/api/tcp.md deleted file mode 100644 index 025ac1e987..0000000000 --- a/sdk/go/docs/src/api/tcp.md +++ /dev/null @@ -1,60 +0,0 @@ -# TCP - -Accessor: `client.TCP()` - -Forward TCP connections to sandbox ports using bidirectional gRPC streaming. - -## Forward - -Open a bidirectional TCP forwarding stream to a specific port inside a sandbox. -Returns an `io.ReadWriteCloser` that proxies data between the caller and the -sandbox port over gRPC streaming. - -```go -conn, err := client.TCP().Forward(ctx, "default", "sandbox-123", 8080) -if err != nil { - log.Fatal(err) -} -defer conn.Close() - -// Exchange protocol bytes with the service through the tunnel. -_, err = conn.Write([]byte("ping\n")) -if err != nil { - log.Fatal(err) -} - -buf := make([]byte, 4096) -n, err := conn.Read(buf) -if err != nil { - log.Fatal(err) -} -fmt.Printf("Response: %s\n", buf[:n]) -``` - -## Listen - -Bind a local address that forwards every connection to a sandbox port. The -returned `ForwardListener` owns its accept loop and all bridge goroutines; it is -a lifecycle handle with `Addr` and `Close`, not a `net.Listener`. - -```go -forward, err := client.TCP().Listen(ctx, "default", "sandbox-123", 8080, 0) -if err != nil { - log.Fatal(err) -} -defer forward.Close() - -conn, err := net.Dial("tcp", forward.Addr().String()) -if err != nil { - log.Fatal(err) -} -defer conn.Close() -``` - -Do not pass `ForwardListener` to `http.Serve`. Dial its address with the client -for the protocol exposed by the sandbox service. - -TCP forwarding is lower-level than [SSH tunneling](ssh.md). Use TCP forwarding -when you need direct port access without SSH session overhead. - -See also: [SSH Tunneling](ssh.md), [Error Handling](../error-handling.md) diff --git a/sdk/go/docs/src/architecture.md b/sdk/go/docs/src/architecture.md deleted file mode 100644 index 0a52481118..0000000000 --- a/sdk/go/docs/src/architecture.md +++ /dev/null @@ -1,139 +0,0 @@ -# Architecture - -This page explains how the OpenShell Go SDK is structured internally. Understanding the design helps you navigate the API surface and write idiomatic code. - -## Client Hierarchy - -The SDK follows the Kubernetes client-go sub-client pattern. A single `Client` provides typed accessors for each API domain: - -```text -Client -├── Sandboxes() → SandboxInterface -├── Exec() → ExecInterface -├── Providers() → ProviderInterface -│ ├── Profiles() → ProfileInterface -│ └── Refresh() → RefreshInterface -├── Services() → ServiceInterface -├── Files() → FileInterface -├── Health() → HealthInterface -├── SSH() → SSHInterface -├── TCP() → TCPInterface -├── Config() → ConfigInterface -└── Policy() → PolicyInterface -``` - -Each accessor returns an interface. You work with the interface, not the concrete implementation. This makes the sub-clients easy to mock and test. - -## Creating a Client - -All interaction starts with `NewClient`: - -```go -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() -``` - -The `Config` struct controls: - -| Field | Purpose | -|-------|---------| -| `Address` | Gateway host and port | -| `Auth` | Authentication provider (`StaticToken`, `NoAuth`, or custom) | -| `TLS` | TLS settings (CA cert, skip verify, client certs) | -| `Retry` | Retry policy for transient failures | - -## Proto Isolation - -The SDK never exposes protobuf-generated types in its public API. Instead, it defines its own Go types (in `openshell/v1/`) and converts to/from proto at the gRPC boundary. - -This means: - -- Your code imports `openshell/v1`, not `proto/openshellv1` -- You work with plain Go structs, not proto messages -- Proto schema changes in upstream OpenShell do not break your code (the SDK adapts internally) -- You can use standard Go patterns (json.Marshal, fmt.Sprintf, reflect) on SDK types without proto constraints - -The conversion layer lives in `openshell/v1/internal/converter/` and is not part of the public API. - -## Sub-Client Pattern - -Each sub-client groups methods for a specific API domain. For example, `SandboxInterface` provides `Create`, `Get`, `List`, `Delete`, `WaitReady`, `Watch`, and more. - -Sub-clients are cheap to access. They are created once when the `Client` is initialized and reuse the same underlying gRPC connection: - -```go -// These return the same sub-client instance every time -sandboxes := client.Sandboxes() -exec := client.Exec() -``` - -Some sub-clients have their own sub-clients. `ProviderInterface` exposes `Profiles()` and `Refresh()`: - -```go -profiles, err := client.Providers().Profiles().ListAll(ctx, "default") -status, err := client.Providers().Refresh().GetStatus(ctx, "default", "openai", "api-key") -``` - -## Functional Options - -Many SDK entry points accept variadic option parameters that configure the call: - -```go -auth, err := v1.RefreshableToken(tokenSource, - v1.WithLeeway(30*time.Second), - v1.WithLogger(logger), -) -``` - -Options are applied in the order they are passed, so later options override earlier ones when they set the same field. **Nil options are silently ignored at every entry point.** You can safely build option lists conditionally without filtering out nil entries: - -```go -opts := []v1.RefreshOption{ - v1.WithLeeway(30 * time.Second), - maybeLogger(), // may return nil -} -auth, err := v1.RefreshableToken(tokenSource, opts...) -``` - -## gRPC Layer - -Underneath, the SDK communicates with the OpenShell gateway over gRPC. The single `OpenShell` service in `proto/openshell.proto` defines all RPCs. The SDK maps each interface method to one or more RPCs: - -| Pattern | Example | -|---------|---------| -| Unary RPC | `Create`, `Get`, `Delete` | -| Server-streaming RPC | `Watch`, `Stream`, `GetLogs` | -| Client-streaming RPC | File uploads | -| Bidirectional streaming | `Interactive`, `Forward` | - -The gRPC connection is managed by the `Client`. Calling `client.Close()` cleanly shuts down all active streams and the underlying connection. - -## Error Model - -All SDK methods return standard Go errors. Errors from the gateway carry a `StatusError` with a typed error code. Use the `Is*` functions to classify errors: - -```go -_, err := client.Sandboxes().Get(ctx, "default", "missing") -if v1.IsNotFound(err) { - // sandbox does not exist -} -``` - -See the [Error Handling](error-handling.md) guide for the complete list of error checks and retry patterns. - -## Fake Client - -For testing, the SDK provides `openshell/v1/fake` with an in-memory implementation of `ClientInterface`. The fake client supports fixture seeding, watch events, and health simulation: - -```go -fc := fake.NewClient() -fc.AddSandbox(&v1.Sandbox{Name: "test-sb", Status: v1.SandboxStatus{Phase: v1.SandboxReady}}) -``` - -See the [Testing](testing.md) guide for complete examples. diff --git a/sdk/go/docs/src/error-handling.md b/sdk/go/docs/src/error-handling.md deleted file mode 100644 index 8288126268..0000000000 --- a/sdk/go/docs/src/error-handling.md +++ /dev/null @@ -1,202 +0,0 @@ -# Error Handling - -Gateway status failures and SDK validation failures use `*v1.StatusError`, which carries a machine-readable `Code` and a human-readable `Message`. Local I/O, transport setup, and sentinel errors may use other Go error types. The SDK provides predicate functions for classified status errors. - -## StatusError - -When an operation returns a classified status error, inspect it directly or use the convenience predicates below. Use `errors.Is` for documented sentinel errors such as `ErrTransportNotAvailable`. - -```go -var se *v1.StatusError -if errors.As(err, &se) { - fmt.Printf("code: %s, message: %s\n", se.Code, se.Message) - // se.ErrorInfo and se.FieldViolations contain optional structured details. -} -``` - -| Field | Type | Description | -|-----------|-------------------|--------------------------------------| -| `Code` | `ErrorCode` | Machine-readable error classification | -| `Message` | `string` | Human-readable error description | -| `GRPCCode` | `int32` | Original gRPC code before SDK classification | -| `FieldViolations` | `[]FieldViolation` | Rejected request fields and descriptions | -| `ErrorInfo` | `*ErrorInfo` | Server reason, domain, and metadata | -| `RetryDelay` | `*time.Duration` | Suggested minimum delay for an otherwise safe retry | -| `Cause` | `error` | Original gRPC error, including unknown details | - -Use `status.FromError(err)` to inspect the complete gRPC status through the SDK's -error wrapper. A delay does not prove that a timed-out mutation was not applied. - -## Predicate Functions - -Use these top-level functions to check error types. They work with wrapped errors via `errors.As`. - -| Function | ErrorCode | When it fires | -|----------------------|----------------------|-------------------------------------------------| -| `v1.IsNotFound` | `ErrorNotFound` | Resource does not exist (sandbox, provider, etc.) | -| `v1.IsAlreadyExists` | `ErrorAlreadyExists` | Resource with that name already exists | -| `v1.IsConflict` | `ErrorConflict` | Optimistic concurrency violation or invalid state transition | -| `v1.IsUnavailable` | `ErrorUnavailable` | Gateway is unreachable or client is closed | -| `v1.IsUnimplemented` | `ErrorUnimplemented` | Operation not supported by the gateway version | -| `v1.IsPermissionDenied` | `ErrorPermissionDenied` | Insufficient permissions | -| `v1.IsInvalidArgument` | `ErrorInvalidArgument` | Invalid request parameters | -| `v1.IsDeadlineExceeded` | `ErrorDeadlineExceeded` | Operation timed out | -| `v1.IsCancelled` | `ErrorCancelled` | Operation was cancelled (context cancellation) | - -For `ErrorInternal` (server-side errors), no convenience predicate exists. Match it directly via the `Code` field: - -```go -var se *v1.StatusError -if errors.As(err, &se) && se.Code == v1.ErrorInternal { - fmt.Println("Internal server error:", se.Message) -} -``` - -## Common Patterns - -### Not Found - -Handle missing resources gracefully: - -```go -sb, err := client.Sandboxes().Get(ctx, "default", "my-sandbox") -if v1.IsNotFound(err) { - fmt.Println("Sandbox does not exist, creating...") - sb, err = client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}) -} -if err != nil { - log.Fatal(err) -} -``` - -### Already Exists - -Guard against duplicate creation: - -```go -_, err := client.Providers().Create(ctx, "default", &v1.Provider{ - Name: "openai", - Type: "openai", -}) -if v1.IsAlreadyExists(err) { - fmt.Println("Provider already registered, skipping") -} else if err != nil { - log.Fatal(err) -} -``` - -Alternatively, use `Ensure` for idempotent registration: - -```go -// Create-or-update in a single call -provider, err := client.Providers().Ensure(ctx, "default", &v1.Provider{ - Name: "openai", - Type: "openai", -}) -``` - -### Conflict (Optimistic Concurrency) - -When two clients modify the same resource concurrently, the second write receives a conflict error. Retry by re-reading the resource: - -```go -sb, _ := client.Sandboxes().Get(ctx, "default", "my-sandbox") - -result, err := client.Sandboxes().AttachProvider(ctx, - "default", "my-sandbox", "openai", sb.ResourceVersion, -) -if v1.IsConflict(err) { - // Another client modified the sandbox — re-read and retry - sb, _ = client.Sandboxes().Get(ctx, "default", "my-sandbox") - result, err = client.Sandboxes().AttachProvider(ctx, - "default", "my-sandbox", "openai", sb.ResourceVersion, - ) -} -if err != nil { - log.Fatal(err) -} -``` - -### Unavailable - -Handle gateway connectivity issues: - -```go -sb, err := client.Sandboxes().Get(ctx, "default", "my-sandbox") -if v1.IsUnavailable(err) { - fmt.Println("Gateway is not reachable, check connection") - // Implement retry with backoff -} -``` - -### Unimplemented - -Detect unsupported operations gracefully: - -```go -_, err := client.Sandboxes().GetLogs(ctx, "default", "my-sandbox") -if v1.IsUnimplemented(err) { - fmt.Println("Log retrieval not supported by this gateway version") -} -``` - -## Retry with Backoff - -For transient errors like `Unavailable` or `DeadlineExceeded`, use exponential backoff only when the operation is idempotent or carries an idempotency/concurrency token. A timeout does not prove that a mutating request was not applied. - -```go -func withRetry(ctx context.Context, maxAttempts int, fn func() error) error { - backoff := 100 * time.Millisecond - - for attempt := 0; attempt < maxAttempts; attempt++ { - err := fn() - if err == nil { - return nil - } - - // Only retry transient errors - if !v1.IsUnavailable(err) && !v1.IsDeadlineExceeded(err) { - return err - } - - // Add jitter to prevent thundering herd after outages - jitter := time.Duration(rand.Int63n(int64(backoff) / 2)) - - select { - case <-ctx.Done(): - return ctx.Err() - case <-time.After(backoff + jitter): - backoff *= 2 - } - } - return fmt.Errorf("exhausted %d retry attempts", maxAttempts) -} -``` - -Usage: - -```go -var sb *v1.Sandbox -err := withRetry(ctx, 3, func() error { - var e error - sb, e = client.Sandboxes().Get(ctx, "default", "my-sandbox") - return e -}) -``` - -## Context Cancellation - -All SDK methods accept a `context.Context`. Use context deadlines and cancellation to control timeouts: - -```go -// 5-second timeout for a single call -ctx, cancel := context.WithTimeout(ctx, 5*time.Second) -defer cancel() - -sb, err := client.Sandboxes().Get(ctx, "default", "my-sandbox") -if v1.IsDeadlineExceeded(err) { - fmt.Println("Request timed out") -} -``` - -See also: [Testing](testing.md) for how the fake client returns the same error codes. diff --git a/sdk/go/docs/src/getting-started.md b/sdk/go/docs/src/getting-started.md deleted file mode 100644 index c59dc450a6..0000000000 --- a/sdk/go/docs/src/getting-started.md +++ /dev/null @@ -1,123 +0,0 @@ -# Quick Start - -This guide walks you through installing the OpenShell Go SDK, connecting to a gateway, creating a sandbox, running a command, and cleaning up. You should be up and running in under 5 minutes. - -## Prerequisites - -- Go 1.25.13 or later -- Access to an OpenShell gateway (address and authentication token) - -## Installation - -Add the SDK to your Go module: - -```bash -go get github.com/NVIDIA/OpenShell/sdk/go@latest -``` - -## Connect to the Gateway - -Create a client by providing the gateway address and authentication credentials: - -```go -package main - -import ( - "context" - "fmt" - "log" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" -) - -func main() { - 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() -``` - -For production use, load the token from an environment variable instead of hardcoding it: -`v1.StaticToken(os.Getenv("OPENSHELL_TOKEN"))` - -The `Config` struct accepts optional fields for TLS configuration and retry policies. For development against a local gateway without TLS, use `v1.NoAuth()` and set TLS to skip verification. - -## Check Gateway Health - -Verify the gateway is reachable: - -```go - ctx := context.Background() - - health, err := client.Health().Check(ctx) - if err != nil { - log.Fatal(err) - } - fmt.Printf("Gateway healthy: %v\n", health.Healthy) -``` - -## Create a Sandbox - -Create a sandbox with a Python image: - -```go - sandbox, err := client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{ - Template: &v1.SandboxTemplate{Image: "python:3.12"}, - Environment: map[string]string{"LANG": "en_US.UTF-8"}, - }) - if err != nil { - log.Fatal(err) - } - fmt.Printf("Created sandbox: %s\n", sandbox.Name) -``` - -## Wait for the Sandbox to be Ready - -Sandboxes take a moment to provision. Use `WaitReady` to block until the sandbox is ready to accept commands: - -```go - sandbox, err = client.Sandboxes().WaitReady(ctx, "default", sandbox.Name) - if err != nil { - log.Fatal(err) - } - fmt.Printf("Sandbox is ready (phase: %s)\n", sandbox.Status.Phase) -``` - -## Run a Command - -Execute a command inside the sandbox: - -```go - result, err := client.Exec().Run(ctx, "default", sandbox.Name, []string{"echo", "hello from OpenShell"}) - if err != nil { - log.Fatal(err) - } - fmt.Printf("Output: %s", result.Stdout) - fmt.Printf("Exit code: %d\n", result.ExitCode) -``` - -For long-running commands, use `Stream` to receive output incrementally, or `Interactive` for terminal-like sessions. - -## Clean Up - -Delete the sandbox when you are done: - -```go - deletion, err := client.Sandboxes().Delete(ctx, "default", sandbox.Name) - if err != nil { - log.Fatal(err) - } - fmt.Printf("Deletion outcome: %v\n", deletion.Outcome) -} -``` - -## Next Steps - -- Browse the [API Overview](api/overview.md) to see all available interfaces -- Learn about [Error Handling](error-handling.md) for production code -- Set up [Testing](testing.md) with the fake client for your test suites -- Explore the [Architecture](architecture.md) to understand the SDK design diff --git a/sdk/go/docs/src/introduction.md b/sdk/go/docs/src/introduction.md deleted file mode 100644 index fbe63dbb7f..0000000000 --- a/sdk/go/docs/src/introduction.md +++ /dev/null @@ -1,34 +0,0 @@ -# OpenShell Go SDK - -The OpenShell Go SDK provides an idiomatic Go client for the OpenShell gateway API. It wraps the underlying gRPC protocol behind typed interfaces, making it straightforward to manage sandboxes, execute commands, handle providers, and more. - -## Key Features - -- **Sub-client pattern**: A single `Client` provides typed accessors for each API domain (Sandboxes, SandboxTemplates, Exec, Providers, Files, Health, SSH, TCP, Config, Policy, Services) -- **Clean types**: SDK types are self-contained, so your code works with idiomatic Go types without extra dependencies -- **Fake client for testing**: An in-memory implementation of the full `ClientInterface` for testing without a live gateway -- **Watch and streaming**: First-class support for watching sandbox state changes and streaming command output -- **Typed error handling**: Functions like `IsNotFound`, `IsAlreadyExists`, and `IsConflict` for precise error classification - -## Where to Start - -If you are new to the SDK, the [Quick Start](getting-started.md) guide walks you through installation, connecting to a gateway, creating your first sandbox, running a command, and cleaning up. - -For a deeper understanding of how the SDK is structured, see the [Architecture](architecture.md) overview. - -## API Reference - -Every SDK interface has a dedicated reference page with method signatures and code examples. - -Browse the full [API Overview](api/overview.md) to see all 13 interfaces at a glance. - -## Guides - -- [Error Handling](error-handling.md): StatusError, typed error checks, retry patterns -- [Testing](testing.md): Fake client usage, fixture seeding, watch event testing - -## Related Projects - -- [**OpenShell**](https://github.com/NVIDIA/OpenShell) (by NVIDIA): The upstream project that defines the gateway API and sandbox runtime this SDK wraps. -- [**openshell-sdk-go**](https://github.com/NVIDIA/OpenShell/sdk/go): This SDK's source repository on GitHub. -- [**pkg.go.dev**](https://pkg.go.dev/github.com/NVIDIA/OpenShell/sdk/go/openshell/v1): Go package documentation with type signatures and godoc. diff --git a/sdk/go/docs/src/testing.md b/sdk/go/docs/src/testing.md deleted file mode 100644 index 3a4c8ee69c..0000000000 --- a/sdk/go/docs/src/testing.md +++ /dev/null @@ -1,184 +0,0 @@ -# Testing - -The SDK ships a `fake` package that provides an in-memory implementation of all client interfaces. Use it in your test suites to exercise SDK interactions without a real gateway. - -```go -import "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/fake" -``` - -The fake client follows the same pattern as `k8s.io/client-go/kubernetes/fake`: it maintains in-memory stores, supports watch event broadcasting, and returns the same `StatusError` codes as the real client. - -## Creating a Fake Client - -```go -func TestMyOperator(t *testing.T) { - client := fake.NewClient() - defer client.Close() - - ctx := context.Background() - - // Use client exactly like the real SDK - sb, err := client.Sandboxes().Create(ctx, "default", "test-sandbox", &v1.SandboxSpec{}) - require.NoError(t, err) - assert.Equal(t, "Provisioning", string(sb.Status.Phase)) -} -``` - -The returned `*fake.Client` satisfies `v1.ClientInterface`, so you can pass it anywhere your code accepts the interface. - -## Fixture Seeding - -Pre-populate the fake client with existing resources before your test runs. Seeded resources are available immediately via `Get` and `List` without going through `Create`. - -### AddSandbox - -```go -client := fake.NewClient() - -// Pre-seed a sandbox that already exists -client.AddSandbox(&types.Sandbox{ - Name: "existing-sandbox", - Status: types.SandboxStatus{ - Phase: types.SandboxReady, - }, - ResourceVersion: 5, -}) - -// Now Get returns it immediately -sb, err := client.Sandboxes().Get(ctx, "default", "existing-sandbox") -// sb.Status.Phase == "Ready" -``` - -### AddProvider - -```go -client := fake.NewClient() - -// Pre-seed a provider -client.AddProvider(&types.Provider{ - Name: "my-openai", - Type: "openai", - Spec: types.ProviderSpec{ - Credentials: map[string]string{ - "api_key": "sk-test-key", - }, - }, -}) - -// List returns the seeded provider -providers, _ := client.Providers().ListAll(ctx, "default") -// len(providers) == 1 -``` - -## Sandbox Lifecycle - -The fake client implements the full sandbox lifecycle. Created sandboxes start in the `Provisioning` phase. Calling `WaitReady` transitions them to `Ready` synchronously. - -```go -client := fake.NewClient() -ctx := context.Background() - -// Create starts in Provisioning -sb, err := client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}) -assert.Equal(t, types.SandboxProvisioning, sb.Status.Phase) - -// WaitReady transitions to Ready (synchronous in fake) -sb, err = client.Sandboxes().WaitReady(ctx, "default", "my-sandbox") -assert.Equal(t, types.SandboxReady, sb.Status.Phase) - -// Delete removes the sandbox -_, err = client.Sandboxes().Delete(ctx, "default", "my-sandbox") -assert.NoError(t, err) - -// Get after delete returns NotFound -_, err = client.Sandboxes().Get(ctx, "default", "my-sandbox") -assert.True(t, v1.IsNotFound(err)) -``` - -## Watch Events - -The fake client broadcasts watch events when resources change. Use watchers to test event-driven code. - -```go -client := fake.NewClient() -ctx := context.Background() - -// Start watching before making changes -watcher, err := client.Sandboxes().Watch(ctx, "default", "my-sandbox") -require.NoError(t, err) -defer watcher.Stop() - -// Create a sandbox — triggers an ADDED event -client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}) - -// Read the event from the channel -event := <-watcher.ResultChan() -assert.Equal(t, types.EventAdded, event.Type) -assert.Equal(t, "my-sandbox", event.Object.Name) -``` - -### StopOnTerminal - -Setting `StopOnTerminal: true` causes the watcher to close automatically when the sandbox reaches a terminal phase (`Ready` or `Error`). - -```go -watcher, err := client.Sandboxes().Watch(ctx, "default", "my-sandbox", v1.WatchOptions{ - StopOnTerminal: true, -}) -require.NoError(t, err) - -// Create and transition to Ready -client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}) -client.Sandboxes().WaitReady(ctx, "default", "my-sandbox") - -// Drain events — channel closes after the Ready event -var events []types.Event[*types.Sandbox] -for ev := range watcher.ResultChan() { - events = append(events, ev) -} -// Channel is now closed -``` - -## Health Simulation - -Use `WithHealthResult` to simulate an unhealthy or degraded gateway. - -```go -// Default: healthy gateway -client := fake.NewClient() -result, _ := client.Health().Check(ctx) -// result.Healthy == true, result.Version == "fake" - -// Simulate unhealthy gateway -client = fake.NewClient(fake.WithHealthResult(&types.HealthResult{ - Healthy: false, - Version: "1.2.3", -})) -result, _ = client.Health().Check(ctx) -// result.Healthy == false -``` - -## Error Behavior - -The fake client returns the same `StatusError` codes as the real client: - -| Scenario | Error Code | -|----------|------------| -| `Get` for a non-existent resource | `ErrorNotFound` | -| `Create` with a duplicate name | `ErrorAlreadyExists` | -| Any call after `Close()` | `ErrorUnavailable` | -| Unimplemented operations (e.g., `GetLogs`) | `ErrorUnimplemented` | - -```go -client := fake.NewClient() -client.Close() - -_, err := client.Sandboxes().Get(ctx, "default", "anything") -assert.True(t, v1.IsUnavailable(err)) -``` - -## Concurrency - -All fake client operations are safe for concurrent use. The internal stores use mutex-based synchronization. This means you can safely use the fake client from multiple goroutines in parallel tests. - -See also: [Error Handling](error-handling.md), [API Reference](api/overview.md) diff --git a/sdk/go/docs/theme/custom.css b/sdk/go/docs/theme/custom.css deleted file mode 100644 index b235309c47..0000000000 --- a/sdk/go/docs/theme/custom.css +++ /dev/null @@ -1,184 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -/* OpenShell Go SDK - Custom Typography - * - * Font: Inter from Google Fonts CDN - * Base size: 18px, line-height: 1.7 - * Max content width: 800px - */ - -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); - -:root { - --content-max-width: none; -} - -/* Base typography */ -body, -.content, -.sidebar { - font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; -} - -.content { - font-size: 18px; - line-height: 1.7; - max-width: none; - padding: 0 4em; -} - -/* Headings */ -.content h1 { - font-size: 2em; - font-weight: 700; - margin-top: 1.5em; - margin-bottom: 0.5em; - line-height: 1.2; -} - -.content h2 { - font-size: 1.5em; - font-weight: 600; - margin-top: 1.8em; - margin-bottom: 0.4em; - line-height: 1.3; - border-bottom: 1px solid var(--sidebar-separator); - padding-bottom: 0.3em; -} - -.content h3 { - font-size: 1.25em; - font-weight: 600; - margin-top: 1.5em; - margin-bottom: 0.3em; - line-height: 1.4; -} - -.content h4 { - font-size: 1.1em; - font-weight: 600; - margin-top: 1.2em; - margin-bottom: 0.3em; -} - -/* Paragraphs */ -.content p { - margin-bottom: 1em; -} - -/* Code blocks */ -.content pre { - font-size: 15px; - line-height: 1.5; - padding: 0; - border-radius: 6px; - margin: 1em 0; - overflow-x: auto; - max-width: 100%; -} - -.content code { - font-size: 0.875em; - padding: 0.15em 0.35em; - border-radius: 3px; -} - -.content pre > code, -.content pre > code.hljs { - font-size: 16px; - padding: 1em 1em !important; - display: block; -} - -/* Tables */ -.content table { - font-size: 16px; - width: 100%; - margin: 1em 0; - border-collapse: collapse; -} - -.content th { - font-weight: 600; - text-align: left; - padding: 0.6em 1em; - border-bottom: 2px solid var(--sidebar-separator); -} - -.content td { - padding: 0.5em 1em; - border-bottom: 1px solid var(--sidebar-separator); -} - -/* Sidebar adjustments */ -.sidebar .sidebar-scrollbox { - font-size: 17px; -} - -/* Hide chapter numbers (mdBook 0.5.x uses inside links) */ -.sidebar ol.chapter li.chapter-item a > strong { - display: none; -} - -/* Main sidebar items */ -.sidebar ol.chapter li.chapter-item { - line-height: 1.7; - margin: 0; - padding: 0; - padding-left: 1em; -} - -/* On-this-page sub-navigation */ -.sidebar .on-this-page li { - line-height: 1.8; - margin: 0; - padding: 0; -} - -.sidebar .on-this-page { - margin-top: 0.3em; -} - -.sidebar .on-this-page ol.section { - padding-left: 1em; - margin: 0; -} - -/* Section headers: clear visual break */ -.sidebar ol.chapter li.part-title { - margin-top: 1.2em; - margin-bottom: 0.4em; - padding-bottom: 0.2em; - font-weight: 700; - font-size: 1.15em; - letter-spacing: 0.03em; - opacity: 0.9; - border-bottom: 1px solid rgba(255, 255, 255, 0.1); -} - -/* Lists */ -.content li { - margin-bottom: 0.3em; -} - -/* Navigation arrows: smaller, less intrusive */ -.nav-chapters { - font-size: 2em; - max-width: 40px; - opacity: 0.4; -} - -.nav-chapters:hover { - opacity: 0.8; -} - -/* Blockquotes */ -.content blockquote { - margin: 1em 0; - padding: 0.5em 1.2em; - border-left: 4px solid var(--sidebar-separator); - font-style: normal; -} diff --git a/sdk/go/go.mod b/sdk/go/go.mod deleted file mode 100644 index 8babaad37f..0000000000 --- a/sdk/go/go.mod +++ /dev/null @@ -1,23 +0,0 @@ -// Deprecated: this module moved to github.com/ai-openshell/sdk-go. -module github.com/NVIDIA/OpenShell/sdk/go - -go 1.25.13 - -require ( - github.com/coder/websocket v1.8.15 - github.com/stretchr/testify v1.11.1 - golang.org/x/oauth2 v0.36.0 - golang.org/x/sync v0.22.0 - google.golang.org/grpc v1.83.2 - google.golang.org/protobuf v1.36.11 -) - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/net v0.58.0 // indirect - golang.org/x/sys v0.47.0 // indirect - golang.org/x/text v0.41.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/sdk/go/go.sum b/sdk/go/go.sum deleted file mode 100644 index 6f325c4d0c..0000000000 --- a/sdk/go/go.sum +++ /dev/null @@ -1,54 +0,0 @@ -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNUA= -github.com/coder/websocket v1.8.15/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= -go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= -go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= -go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= -go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= -go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= -go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= -go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= -go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= -go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= -golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= -golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= -golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= -golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= -golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= -golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= -golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= -gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= -gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= -google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/sdk/go/mise.toml b/sdk/go/mise.toml deleted file mode 100644 index 2618b146ee..0000000000 --- a/sdk/go/mise.toml +++ /dev/null @@ -1,154 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -[tools] -go = "1.25.14" -buf = "1.72.0" -"go:github.com/golangci/golangci-lint/v2/cmd/golangci-lint" = "2.12" -"go:google.golang.org/protobuf/cmd/protoc-gen-go" = "1.36.11" -"go:google.golang.org/grpc/cmd/protoc-gen-go-grpc" = "1.6.2" - -[tasks.test] -description = "Run unit tests with coverage" -run = "go test -coverprofile=coverage.out -coverpkg=./openshell/... -race ./..." - -[tasks."test:integration"] -description = "Run integration tests" -run = "go test -tags=integration -race ./..." - -[tasks.lint] -description = "Run linter" -run = "golangci-lint run ./..." - -[tasks.fmt] -description = "Format code" -run = "goimports -w . && go fmt ./..." - -[tasks.build] -description = "Build all packages" -run = "go build ./..." - -[tasks.ci] -description = "Run full CI pipeline" -depends = ["lint", "build", "test", "proto:check", "docs:check"] - -[tasks."docs:check"] -description = "Verify every public package has a docs page" -run = """ -#!/usr/bin/env bash -set -euo pipefail - -DOCS_DIR="docs/src/api" -SUMMARY="docs/src/SUMMARY.md" -MISSING=0 - -# Find all public packages with a doc.go (excluding internal, proto, types) -for docfile in openshell/v1/*/doc.go; do - pkg=$(basename "$(dirname "$docfile")") - - # Skip internal packages and types (no user-facing docs needed) - case "$pkg" in - internal|types) continue ;; - esac - - # Check for matching docs page - if [ ! -f "$DOCS_DIR/$pkg.md" ]; then - echo "MISSING: $DOCS_DIR/$pkg.md (package openshell/v1/$pkg has doc.go but no docs page)" - MISSING=$((MISSING + 1)) - fi - - # Check for SUMMARY.md entry - if ! grep -q "api/$pkg.md" "$SUMMARY" 2>/dev/null; then - echo "MISSING: SUMMARY.md entry for api/$pkg.md" - MISSING=$((MISSING + 1)) - fi -done - -if [ "$MISSING" -gt 0 ]; then - echo "" - echo "ERROR: $MISSING documentation gaps found." - echo "Every public package with doc.go needs a docs/src/api/.md page" - echo "and a SUMMARY.md entry. See Constitution XIII." - exit 1 -fi - -echo "Docs check passed: all public packages have documentation." -""" - -[tasks."proto:gen"] -description = "Generate Go bindings from proto files using buf" -run = """ -#!/usr/bin/env bash -set -euo pipefail - -SDK_ROOT=$(pwd -P) -REPO_ROOT=$(cd ../.. && pwd -P) - -for tool in buf protoc-gen-go protoc-gen-go-grpc; do - if ! command -v "$tool" &>/dev/null; then - echo "ERROR: $tool not found. Run 'mise install' to install it." - exit 1 - fi -done - -if find proto -maxdepth 1 -name '*.proto' -print -quit | grep -q .; then - echo "ERROR: sdk/go/proto must contain generated bindings only." - echo "Proto sources belong in the repository root proto/ directory." - exit 1 -fi - -# Clean previous output before regeneration -find proto -name '*.pb.go' -delete 2>/dev/null || true -find proto -mindepth 1 -type d -empty -delete 2>/dev/null || true - -(cd "$REPO_ROOT" && buf generate --template "$SDK_ROOT/buf.gen.yaml") - -echo "Proto generation complete." -echo "Generated packages:" -for pkg_dir in proto/*/; do - count=$(find "$pkg_dir" -maxdepth 1 -name '*.go' | wc -l | tr -d ' ') - echo " $pkg_dir: $count files" -done -""" - -[tasks."proto:check"] -description = "Verify generated proto files are up to date" -run = """ -#!/usr/bin/env bash -set -euo pipefail - -SDK_ROOT=$(pwd -P) -REPO_ROOT=$(cd ../.. && pwd -P) - -for tool in buf protoc-gen-go protoc-gen-go-grpc; do - if ! command -v "$tool" &>/dev/null; then - echo "ERROR: $tool not found. Run 'mise install' to install it." - exit 1 - fi -done - -WORK_DIR=$(mktemp -d) -trap 'rm -rf "$WORK_DIR"' EXIT - -if find proto -maxdepth 1 -name '*.proto' -print -quit | grep -q .; then - echo "ERROR: sdk/go/proto contains copied proto sources." - echo "Proto sources belong in the repository root proto/ directory." - exit 1 -fi - -# Generate to temp directory with adjusted output path -CHECK_TEMPLATE=$(sed 's|out: sdk/go|out: '"$WORK_DIR"'|' buf.gen.yaml) -(cd "$REPO_ROOT" && buf generate --template "$CHECK_TEMPLATE") - -DIFF_OUTPUT=$(diff -r "$WORK_DIR/proto" "$SDK_ROOT/proto" 2>&1) || true - -if [ -n "$DIFF_OUTPUT" ]; then - echo "ERROR: Generated proto files are out of date." - echo "Run 'mise run proto:gen' to regenerate." - echo "" - echo "$DIFF_OUTPUT" - exit 1 -fi - -echo "Proto check passed: generated files are up to date." -""" diff --git a/sdk/go/openshell/v1/auth.go b/sdk/go/openshell/v1/auth.go deleted file mode 100644 index 95a7838f08..0000000000 --- a/sdk/go/openshell/v1/auth.go +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// AuthProvider supplies per-RPC credentials. It implements the -// grpc credentials.PerRPCCredentials interface. -type AuthProvider = types.AuthProvider - -type noAuth struct{} - -// NoAuth returns an AuthProvider that sends no credentials. -func NoAuth() AuthProvider { - return &noAuth{} -} - -func (n *noAuth) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) { - return nil, nil -} - -func (n *noAuth) RequireTransportSecurity() bool { - return false -} - -type staticToken struct { - token string -} - -// StaticToken returns an AuthProvider that sends a fixed Bearer token. -func StaticToken(token string) AuthProvider { - return &staticToken{token: token} -} - -func (s *staticToken) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) { - return map[string]string{ - "authorization": "Bearer " + s.token, - }, nil -} - -func (s *staticToken) RequireTransportSecurity() bool { - return true -} diff --git a/sdk/go/openshell/v1/auth_extra.go b/sdk/go/openshell/v1/auth_extra.go deleted file mode 100644 index 21a9e9e2cf..0000000000 --- a/sdk/go/openshell/v1/auth_extra.go +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "errors" - "maps" - "strings" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// extraHeadersAuth wraps a base AuthProvider with additional static headers -// that are merged into every GetRequestMetadata call. Extra headers take -// precedence over base headers on key collision (case-insensitive). -type extraHeadersAuth struct { - base types.AuthProvider - headers map[string]string // keys already lowercase, empty values filtered out -} - -// WithExtraHeaders wraps base with additional per-RPC headers. Keys are -// normalized to lowercase per HTTP/2 (RFC 9113). Empty-string values are -// silently dropped. The headers map is deep-copied at construction time, -// so later mutations to the caller's map have no effect. -// -// Returns an error if base is nil or if headers is nil, empty, or contains -// only empty-string values. -func WithExtraHeaders(base AuthProvider, headers map[string]string) (AuthProvider, error) { - if base == nil { - return nil, errors.New("base auth provider must not be nil") - } - if len(headers) == 0 { - return nil, errors.New("headers must not be nil or empty") - } - - // Deep-copy and normalize: lowercase keys, skip empty values. - normalized := make(map[string]string, len(headers)) - for k, v := range headers { - if v == "" { - continue - } - normalized[strings.ToLower(k)] = v - } - - if len(normalized) == 0 { - return nil, errors.New("headers must contain at least one non-empty value") - } - - return &extraHeadersAuth{ - base: base, - headers: normalized, - }, nil -} - -// GetRequestMetadata merges base metadata with extra headers. Extra headers -// win on key collision because they are applied after the base metadata. -func (e *extraHeadersAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { - baseMD, err := e.base.GetRequestMetadata(ctx, uri...) - if err != nil { - return nil, err - } - - // Start with base metadata (may be nil for NoAuth). - // Normalize base keys to lowercase for case-insensitive collision. - merged := make(map[string]string, len(baseMD)+len(e.headers)) - for k, v := range baseMD { - merged[strings.ToLower(k)] = v - } - // Extra headers overwrite base on collision. - maps.Copy(merged, e.headers) - - return merged, nil -} - -// RequireTransportSecurity delegates to the base auth provider. -func (e *extraHeadersAuth) RequireTransportSecurity() bool { - return e.base.RequireTransportSecurity() -} diff --git a/sdk/go/openshell/v1/auth_extra_test.go b/sdk/go/openshell/v1/auth_extra_test.go deleted file mode 100644 index 09bd4d33d2..0000000000 --- a/sdk/go/openshell/v1/auth_extra_test.go +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "errors" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestWithExtraHeaders_NilBase(t *testing.T) { - _, err := WithExtraHeaders(nil, map[string]string{"x-key": "val"}) - require.Error(t, err) - assert.Contains(t, err.Error(), "base") -} - -func TestWithExtraHeaders_NilHeaders(t *testing.T) { - _, err := WithExtraHeaders(NoAuth(), nil) - require.Error(t, err) - assert.Contains(t, err.Error(), "headers") -} - -func TestWithExtraHeaders_EmptyHeaders(t *testing.T) { - _, err := WithExtraHeaders(NoAuth(), map[string]string{}) - require.Error(t, err) - assert.Contains(t, err.Error(), "headers") -} - -func TestWithExtraHeaders_AllEmptyValues(t *testing.T) { - // All values are empty strings, so after filtering, headers map is empty. - _, err := WithExtraHeaders(NoAuth(), map[string]string{"x-key": ""}) - require.Error(t, err) - assert.Contains(t, err.Error(), "headers") -} - -func TestWithExtraHeaders_MergesWithBase(t *testing.T) { - base := StaticToken("my-token") - auth, err := WithExtraHeaders(base, map[string]string{ - "x-proxy-key": "proxy-secret", - "x-tenant-id": "acme-corp", - }) - require.NoError(t, err) - - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - assert.Equal(t, "Bearer my-token", md["authorization"]) - assert.Equal(t, "proxy-secret", md["x-proxy-key"]) - assert.Equal(t, "acme-corp", md["x-tenant-id"]) -} - -func TestWithExtraHeaders_ExtraPrecedenceOnCollision(t *testing.T) { - base := StaticToken("my-token") - auth, err := WithExtraHeaders(base, map[string]string{ - "authorization": "Custom override-token", - }) - require.NoError(t, err) - - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - // Extra header wins over base. - assert.Equal(t, "Custom override-token", md["authorization"]) -} - -func TestWithExtraHeaders_CaseInsensitiveCollision(t *testing.T) { - base := StaticToken("my-token") - // "Authorization" with uppercase should still override "authorization". - auth, err := WithExtraHeaders(base, map[string]string{ - "Authorization": "Custom override-token", - }) - require.NoError(t, err) - - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - assert.Equal(t, "Custom override-token", md["authorization"]) -} - -func TestWithExtraHeaders_EmptyValueSkipped(t *testing.T) { - base := StaticToken("my-token") - auth, err := WithExtraHeaders(base, map[string]string{ - "x-proxy-key": "proxy-secret", - "x-empty": "", // Should be silently skipped. - }) - require.NoError(t, err) - - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - assert.Equal(t, "proxy-secret", md["x-proxy-key"]) - _, hasEmpty := md["x-empty"] - assert.False(t, hasEmpty, "empty-string header values should be skipped") -} - -func TestWithExtraHeaders_RequireTransportSecurity_Delegates(t *testing.T) { - tests := []struct { - name string - base AuthProvider - expected bool - }{ - { - name: "delegates to NoAuth (false)", - base: NoAuth(), - expected: false, - }, - { - name: "delegates to StaticToken (true)", - base: StaticToken("tok"), - expected: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - auth, err := WithExtraHeaders(tt.base, map[string]string{"x-key": "val"}) - require.NoError(t, err) - assert.Equal(t, tt.expected, auth.RequireTransportSecurity()) - }) - } -} - -func TestWithExtraHeaders_WithNoAuth(t *testing.T) { - auth, err := WithExtraHeaders(NoAuth(), map[string]string{ - "x-proxy-key": "proxy-secret", - }) - require.NoError(t, err) - - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - // NoAuth returns nil metadata, extra headers should still appear. - assert.Equal(t, "proxy-secret", md["x-proxy-key"]) -} - -func TestWithExtraHeaders_BaseError_Propagated(t *testing.T) { - base := &errAuth{err: errors.New("auth failure")} - auth, err := WithExtraHeaders(base, map[string]string{"x-key": "val"}) - require.NoError(t, err) - - _, err = auth.GetRequestMetadata(context.Background()) - require.Error(t, err) - assert.Contains(t, err.Error(), "auth failure") -} - -func TestWithExtraHeaders_DeepCopiesHeaders(t *testing.T) { - original := map[string]string{ - "x-key": "original-value", - } - auth, err := WithExtraHeaders(NoAuth(), original) - require.NoError(t, err) - - // Mutate the original map after construction. - original["x-key"] = "mutated-value" - - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - // The wrapper should use the value at construction time, not the mutated value. - assert.Equal(t, "original-value", md["x-key"]) -} - -// errAuth is a test helper that always returns an error. -type errAuth struct { - err error -} - -func (e *errAuth) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) { - return nil, e.err -} - -func (e *errAuth) RequireTransportSecurity() bool { - return false -} diff --git a/sdk/go/openshell/v1/auth_refresh.go b/sdk/go/openshell/v1/auth_refresh.go deleted file mode 100644 index 5cc9fbf2fd..0000000000 --- a/sdk/go/openshell/v1/auth_refresh.go +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "errors" - "sync" - "time" - - "golang.org/x/oauth2" - "golang.org/x/sync/singleflight" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -const ( - defaultLeeway = 10 * time.Second - initialBackoff = 1 * time.Second - maxBackoff = 30 * time.Second -) - -var errNilTokenSource = errors.New("openshell: TokenSource must not be nil") - -// RefreshOption configures the behavior of RefreshableToken. -type RefreshOption func(*refreshConfig) - -type refreshConfig struct { - leeway time.Duration - logger types.Logger -} - -func defaultRefreshConfig() refreshConfig { - return refreshConfig{ - leeway: defaultLeeway, - } -} - -// WithLeeway sets the duration before token expiry at which a proactive -// refresh is triggered. Default is 10 seconds. -func WithLeeway(d time.Duration) RefreshOption { - return func(c *refreshConfig) { - if d < 0 { - d = 0 - } - c.leeway = d - } -} - -// WithLogger sets the logger used for stale-token fallback warnings. -// When not set, warnings are silently dropped. -func WithLogger(l types.Logger) RefreshOption { - return func(c *refreshConfig) { - c.logger = l - } -} - -type refreshableAuth struct { - source oauth2.TokenSource - mu sync.Mutex - group singleflight.Group - tok *oauth2.Token - leeway time.Duration - logger types.Logger - nextRetry time.Time - backoff time.Duration - lastErr error -} - -// usableCached reports whether the cached token has not yet expired. -// Callers must hold r.mu. -func (r *refreshableAuth) usableCached() bool { - return r.tok != nil && (r.tok.Expiry.IsZero() || time.Now().Before(r.tok.Expiry)) -} - -func (r *refreshableAuth) isTokenValid() bool { - if r.tok == nil { - return false - } - if r.tok.Expiry.IsZero() { - return true - } - return time.Now().Before(r.tok.Expiry.Add(-r.leeway)) -} - -func (r *refreshableAuth) GetRequestMetadata(ctx context.Context, _ ...string) (map[string]string, error) { - r.mu.Lock() - if r.isTokenValid() { - tok := r.tok.AccessToken - r.mu.Unlock() - return map[string]string{"authorization": "Bearer " + tok}, nil - } - - if !r.nextRetry.IsZero() && time.Now().Before(r.nextRetry) { - if r.usableCached() { - tok := r.tok.AccessToken - r.mu.Unlock() - return map[string]string{"authorization": "Bearer " + tok}, nil - } - err := r.lastErr - r.mu.Unlock() - return nil, err - } - r.mu.Unlock() - - resultCh := r.group.DoChan("refresh", func() (any, error) { - return r.source.Token() - }) - var val any - var err error - select { - case <-ctx.Done(): - return nil, ctx.Err() - case result := <-resultCh: - val = result.Val - err = result.Err - } - - r.mu.Lock() - defer r.mu.Unlock() - - if err != nil { - if r.nextRetry.IsZero() || !time.Now().Before(r.nextRetry) { - bo := r.backoff - if bo == 0 { - bo = initialBackoff - } else { - bo *= 2 - if bo > maxBackoff { - bo = maxBackoff - } - } - r.backoff = bo - r.nextRetry = time.Now().Add(bo) - } - r.lastErr = err - - if r.usableCached() { - if r.logger != nil { - r.logger.Error(err, "token refresh failed, using cached token") - } - return map[string]string{"authorization": "Bearer " + r.tok.AccessToken}, nil - } - return nil, err - } - - tok, ok := val.(*oauth2.Token) - if !ok || tok == nil { - return nil, errors.New("openshell: token source returned nil token without error") - } - r.tok = tok - r.backoff = 0 - r.nextRetry = time.Time{} - r.lastErr = nil - return map[string]string{"authorization": "Bearer " + r.tok.AccessToken}, nil -} - -func (r *refreshableAuth) RequireTransportSecurity() bool { - return true -} - -// RefreshableToken returns an AuthProvider that caches tokens from src -// and refreshes them before expiry. Concurrent callers share a single -// in-flight refresh via singleflight. Failed refreshes trigger exponential -// backoff (1s, 2s, 4s, ..., 30s cap) to avoid amplifying token endpoint outages. -// After a failed refresh, the cached token is used only until it expires; -// after that, requests fail with the last refresh error. -func RefreshableToken(src oauth2.TokenSource, opts ...RefreshOption) (AuthProvider, error) { - if src == nil { - return nil, errNilTokenSource - } - - cfg := defaultRefreshConfig() - options.Apply(&cfg, opts) - - return &refreshableAuth{ - source: src, - leeway: cfg.leeway, - logger: cfg.logger, - }, nil -} diff --git a/sdk/go/openshell/v1/auth_refresh_test.go b/sdk/go/openshell/v1/auth_refresh_test.go deleted file mode 100644 index 69fde8b118..0000000000 --- a/sdk/go/openshell/v1/auth_refresh_test.go +++ /dev/null @@ -1,571 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "errors" - "fmt" - "sync" - "sync/atomic" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "golang.org/x/oauth2" -) - -// mockTokenSource implements oauth2.TokenSource for testing. -type mockTokenSource struct { - mu sync.Mutex - tokenFunc func() (*oauth2.Token, error) - callCount int -} - -func (m *mockTokenSource) Token() (*oauth2.Token, error) { - m.mu.Lock() - m.callCount++ - m.mu.Unlock() - return m.tokenFunc() -} - -func (m *mockTokenSource) calls() int { - m.mu.Lock() - defer m.mu.Unlock() - return m.callCount -} - -// --- Phase 2 tests: constructor validation --- - -func TestRefreshableToken_NilSource(t *testing.T) { - _, err := RefreshableToken(nil) - require.Error(t, err) - assert.Equal(t, "openshell: TokenSource must not be nil", err.Error()) -} - -func TestRefreshableToken_ValidSource(t *testing.T) { - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - return &oauth2.Token{AccessToken: "tok", Expiry: time.Now().Add(time.Hour)}, nil - }, - } - provider, err := RefreshableToken(src) - require.NoError(t, err) - assert.NotNil(t, provider) -} - -func TestGetRequestMetadata_ReturnsWhenContextCanceledDuringRefresh(t *testing.T) { - release := make(chan struct{}) - t.Cleanup(func() { close(release) }) - src := &mockTokenSource{tokenFunc: func() (*oauth2.Token, error) { - <-release - return &oauth2.Token{AccessToken: "late-token"}, nil - }} - provider, err := RefreshableToken(src) - require.NoError(t, err) - - ctx, cancel := context.WithCancel(context.Background()) - cancel() - started := time.Now() - _, err = provider.GetRequestMetadata(ctx) - - require.ErrorIs(t, err, context.Canceled) - assert.Less(t, time.Since(started), time.Second) -} - -// --- Phase 3 / US1 tests: automatic token refresh --- - -func TestGetRequestMetadata_FirstCallFetchesToken(t *testing.T) { - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - return &oauth2.Token{AccessToken: "fresh-token", Expiry: time.Now().Add(time.Hour)}, nil - }, - } - provider, err := RefreshableToken(src) - require.NoError(t, err) - - md, err := provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer fresh-token", md["authorization"]) - assert.Equal(t, 1, src.calls()) -} - -func TestGetRequestMetadata_CachedTokenNoExtraCall(t *testing.T) { - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - return &oauth2.Token{AccessToken: "cached-token", Expiry: time.Now().Add(time.Hour)}, nil - }, - } - provider, err := RefreshableToken(src) - require.NoError(t, err) - - _, err = provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - md, err := provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer cached-token", md["authorization"]) - assert.Equal(t, 1, src.calls(), "second call should use cache, not invoke TokenSource") -} - -func TestGetRequestMetadata_RefreshesExpiredToken(t *testing.T) { - var callNum atomic.Int32 - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - n := callNum.Add(1) - if n == 1 { - return &oauth2.Token{AccessToken: "old", Expiry: time.Now().Add(-time.Minute)}, nil - } - return &oauth2.Token{AccessToken: "new", Expiry: time.Now().Add(time.Hour)}, nil - }, - } - provider, err := RefreshableToken(src, WithLeeway(0)) - require.NoError(t, err) - - // First call gets the expired token, which is immediately stale. - md, err := provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer old", md["authorization"]) - - // Second call should trigger a refresh since the cached token is expired. - md, err = provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer new", md["authorization"]) - assert.Equal(t, 2, src.calls()) -} - -func TestGetRequestMetadata_ConcurrentSingleFlight(t *testing.T) { - var fetchCount atomic.Int32 - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - fetchCount.Add(1) - time.Sleep(100 * time.Millisecond) - return &oauth2.Token{AccessToken: "shared-token", Expiry: time.Now().Add(time.Hour)}, nil - }, - } - provider, err := RefreshableToken(src) - require.NoError(t, err) - - const goroutines = 20 - var wg sync.WaitGroup - wg.Add(goroutines) - ready := make(chan struct{}) - results := make([]string, goroutines) - errs := make([]error, goroutines) - - for i := range goroutines { - go func(idx int) { - defer wg.Done() - <-ready - md, e := provider.GetRequestMetadata(context.Background()) - errs[idx] = e - if md != nil { - results[idx] = md["authorization"] - } - }(i) - } - close(ready) - wg.Wait() - - for i := range goroutines { - require.NoError(t, errs[i], "goroutine %d failed", i) - assert.Equal(t, "Bearer shared-token", results[i], "goroutine %d got wrong token", i) - } - assert.Equal(t, int32(1), fetchCount.Load(), "expected exactly 1 TokenSource.Token() call, got %d", fetchCount.Load()) -} - -func TestRefreshableAuth_RequireTransportSecurity(t *testing.T) { - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - return &oauth2.Token{AccessToken: "t"}, nil - }, - } - provider, err := RefreshableToken(src) - require.NoError(t, err) - assert.True(t, provider.RequireTransportSecurity()) -} - -// --- Phase 4 / US2 tests: graceful degradation --- - -func TestGetRequestMetadata_RefreshFailureReturnsStaleCachedToken(t *testing.T) { - var callNum atomic.Int32 - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - n := callNum.Add(1) - if n == 1 { - return &oauth2.Token{AccessToken: "stale", Expiry: time.Now().Add(5 * time.Second)}, nil - } - return nil, fmt.Errorf("idp unavailable") - }, - } - provider, err := RefreshableToken(src) // default 10s leeway - require.NoError(t, err) - - // First call succeeds with a token inside the leeway window. - _, err = provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - // Second call: refresh fails, should return the unexpired stale token. - md, err := provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer stale", md["authorization"]) -} - -func TestGetRequestMetadata_RefreshFailureWithExpiredCachedTokenReturnsError(t *testing.T) { - errIDP := errors.New("idp unavailable") - var callNum atomic.Int32 - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - n := callNum.Add(1) - if n == 1 { - return &oauth2.Token{AccessToken: "expired", Expiry: time.Now().Add(-time.Minute)}, nil - } - return nil, errIDP - }, - } - logger := &captureLogger{} - provider, err := RefreshableToken(src, WithLeeway(0), WithLogger(logger)) - require.NoError(t, err) - - _, err = provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - md, err := provider.GetRequestMetadata(context.Background()) - require.ErrorIs(t, err, errIDP) - assert.Nil(t, md) - assert.Empty(t, logger.errors) - - // During backoff, the same error is returned without calling the source. - md, err = provider.GetRequestMetadata(context.Background()) - require.ErrorIs(t, err, errIDP) - assert.Nil(t, md) - assert.Equal(t, 2, src.calls()) -} - -func TestGetRequestMetadata_RefreshFailureLogsWarning(t *testing.T) { - var callNum atomic.Int32 - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - n := callNum.Add(1) - if n == 1 { - return &oauth2.Token{AccessToken: "stale", Expiry: time.Now().Add(5 * time.Second)}, nil - } - return nil, fmt.Errorf("idp unavailable") - }, - } - - logger := &captureLogger{} - provider, err := RefreshableToken(src, WithLogger(logger)) - require.NoError(t, err) - - _, _ = provider.GetRequestMetadata(context.Background()) - _, _ = provider.GetRequestMetadata(context.Background()) - - require.Len(t, logger.errors, 1) - assert.Contains(t, logger.errors[0].msg, "token refresh failed") -} - -func TestGetRequestMetadata_RefreshFailureNoCachedTokenReturnsError(t *testing.T) { - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - return nil, fmt.Errorf("idp unavailable") - }, - } - provider, err := RefreshableToken(src) - require.NoError(t, err) - - _, err = provider.GetRequestMetadata(context.Background()) - require.Error(t, err) - assert.Contains(t, err.Error(), "idp unavailable") -} - -func TestGetRequestMetadata_RefreshFailureNoLoggerNoPanic(t *testing.T) { - var callNum atomic.Int32 - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - n := callNum.Add(1) - if n == 1 { - return &oauth2.Token{AccessToken: "stale", Expiry: time.Now().Add(5 * time.Second)}, nil - } - return nil, fmt.Errorf("idp unavailable") - }, - } - provider, err := RefreshableToken(src) - require.NoError(t, err) - - _, _ = provider.GetRequestMetadata(context.Background()) - - assert.NotPanics(t, func() { - _, _ = provider.GetRequestMetadata(context.Background()) - }) -} - -// --- Phase 5 / US3 tests: configurable leeway --- - -func TestGetRequestMetadata_DefaultLeewayTriggersRefresh(t *testing.T) { - var callNum atomic.Int32 - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - n := callNum.Add(1) - return &oauth2.Token{ - AccessToken: fmt.Sprintf("token-%d", n), - Expiry: time.Now().Add(5 * time.Second), // within default 10s leeway - }, nil - }, - } - provider, err := RefreshableToken(src) // default 10s leeway - require.NoError(t, err) - - _, err = provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - // Token expires in 5s, which is within 10s leeway, so next call should refresh. - md, err := provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer token-2", md["authorization"]) - assert.Equal(t, 2, src.calls()) -} - -func TestGetRequestMetadata_CustomLeewayTriggersRefresh(t *testing.T) { - var callNum atomic.Int32 - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - n := callNum.Add(1) - return &oauth2.Token{ - AccessToken: fmt.Sprintf("token-%d", n), - Expiry: time.Now().Add(25 * time.Second), // within custom 30s leeway - }, nil - }, - } - provider, err := RefreshableToken(src, WithLeeway(30*time.Second)) - require.NoError(t, err) - - _, err = provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - // Token expires in 25s, which is within 30s leeway, so next call should refresh. - md, err := provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer token-2", md["authorization"]) - assert.Equal(t, 2, src.calls()) -} - -func TestGetRequestMetadata_ZeroExpiryNeverRefreshes(t *testing.T) { - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - return &oauth2.Token{AccessToken: "forever-token"}, nil // zero Expiry - }, - } - provider, err := RefreshableToken(src) - require.NoError(t, err) - - _, err = provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - // Call multiple times; should never refresh since expiry is zero. - for range 10 { - _, err = provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - } - assert.Equal(t, 1, src.calls(), "zero-expiry token should never be refreshed") -} - -// --- Backoff tests --- - -func TestGetRequestMetadata_BackoffSkipsRefreshDuringWindow(t *testing.T) { - var callNum atomic.Int32 - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - n := callNum.Add(1) - if n == 1 { - return &oauth2.Token{AccessToken: "stale", Expiry: time.Now().Add(5 * time.Second)}, nil - } - return nil, fmt.Errorf("idp down") - }, - } - provider, err := RefreshableToken(src) - require.NoError(t, err) - - _, err = provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - _, err = provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - beforeCount := src.calls() - require.Equal(t, 2, beforeCount) - - ra := provider.(*refreshableAuth) - ra.mu.Lock() - ra.nextRetry = time.Now().Add(time.Minute) - ra.mu.Unlock() - - md, err := provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer stale", md["authorization"]) - assert.Equal(t, beforeCount, src.calls(), "should not call Token() during backoff window") -} - -func TestGetRequestMetadata_BackoffWithoutCachedTokenReturnsLastError(t *testing.T) { - errIDP := errors.New("idp down") - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - return nil, errIDP - }, - } - provider, err := RefreshableToken(src) - require.NoError(t, err) - - _, err = provider.GetRequestMetadata(context.Background()) - require.ErrorIs(t, err, errIDP) - - _, err = provider.GetRequestMetadata(context.Background()) - require.ErrorIs(t, err, errIDP) - assert.Equal(t, 1, src.calls(), "should not call Token() during backoff window") -} - -func TestGetRequestMetadata_BackoffResetsOnSuccess(t *testing.T) { - var callNum atomic.Int32 - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - n := callNum.Add(1) - switch n { - case 1: - return &oauth2.Token{AccessToken: "initial", Expiry: time.Now().Add(-time.Second)}, nil - case 2: - return nil, fmt.Errorf("fail once") - default: - return &oauth2.Token{AccessToken: "recovered", Expiry: time.Now().Add(time.Hour)}, nil - } - }, - } - provider, err := RefreshableToken(src, WithLeeway(0)) - require.NoError(t, err) - - _, _ = provider.GetRequestMetadata(context.Background()) - _, _ = provider.GetRequestMetadata(context.Background()) - - ra := provider.(*refreshableAuth) - ra.mu.Lock() - ra.nextRetry = time.Time{} - ra.mu.Unlock() - - md, err := provider.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer recovered", md["authorization"]) - - ra.mu.Lock() - assert.Equal(t, time.Duration(0), ra.backoff, "backoff should reset after success") - assert.True(t, ra.nextRetry.IsZero(), "nextRetry should be zero after success") - ra.mu.Unlock() -} - -func TestGetRequestMetadata_BackoffCapsAt30s(t *testing.T) { - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - return nil, fmt.Errorf("always fail") - }, - } - provider, err := RefreshableToken(src, WithLeeway(0)) - require.NoError(t, err) - - ra := provider.(*refreshableAuth) - - for range 10 { - ra.mu.Lock() - ra.nextRetry = time.Time{} - ra.mu.Unlock() - - _, _ = provider.GetRequestMetadata(context.Background()) - } - - ra.mu.Lock() - assert.Equal(t, maxBackoff, ra.backoff, "backoff should cap at maxBackoff") - ra.mu.Unlock() -} - -func TestGetRequestMetadata_ConcurrentFailureBackoffNotOverIncremented(t *testing.T) { - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - time.Sleep(10 * time.Millisecond) - return nil, fmt.Errorf("idp down") - }, - } - provider, err := RefreshableToken(src, WithLeeway(0)) - require.NoError(t, err) - - const goroutines = 20 - var wg sync.WaitGroup - wg.Add(goroutines) - ready := make(chan struct{}) - for range goroutines { - go func() { - defer wg.Done() - <-ready - _, _ = provider.GetRequestMetadata(context.Background()) - }() - } - close(ready) - wg.Wait() - - ra := provider.(*refreshableAuth) - ra.mu.Lock() - assert.Equal(t, initialBackoff, ra.backoff, - "a single coalesced failure should set backoff to initialBackoff, not escalate") - ra.mu.Unlock() - assert.Equal(t, 1, src.calls(), "singleflight should coalesce to 1 call") -} - -// --- benchmarks --- - -func BenchmarkGetRequestMetadata_CachedToken(b *testing.B) { - src := &mockTokenSource{ - tokenFunc: func() (*oauth2.Token, error) { - return &oauth2.Token{AccessToken: "bench-token", Expiry: time.Now().Add(time.Hour)}, nil - }, - } - provider, err := RefreshableToken(src) - require.NoError(b, err) - - // Prime the cache. - _, err = provider.GetRequestMetadata(context.Background()) - require.NoError(b, err) - - b.ResetTimer() - b.ReportAllocs() - for range b.N { - _, _ = provider.GetRequestMetadata(context.Background()) - } -} - -// --- helpers --- - -type logEntry struct { - err error - msg string -} - -type captureLogger struct { - mu sync.Mutex - debugs []string - infos []string - errors []logEntry -} - -func (l *captureLogger) Debug(msg string, _ ...any) { - l.mu.Lock() - defer l.mu.Unlock() - l.debugs = append(l.debugs, msg) -} - -func (l *captureLogger) Info(msg string, _ ...any) { - l.mu.Lock() - defer l.mu.Unlock() - l.infos = append(l.infos, msg) -} - -func (l *captureLogger) Error(err error, msg string, _ ...any) { - l.mu.Lock() - defer l.mu.Unlock() - l.errors = append(l.errors, logEntry{err: err, msg: msg}) -} diff --git a/sdk/go/openshell/v1/auth_test.go b/sdk/go/openshell/v1/auth_test.go deleted file mode 100644 index 2981fd7921..0000000000 --- a/sdk/go/openshell/v1/auth_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestNoAuth_GetRequestMetadata(t *testing.T) { - auth := NoAuth() - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Empty(t, md) -} - -func TestNoAuth_RequireTransportSecurity(t *testing.T) { - auth := NoAuth() - assert.False(t, auth.RequireTransportSecurity()) -} - -func TestStaticToken_GetRequestMetadata(t *testing.T) { - auth := StaticToken("my-secret-token") - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer my-secret-token", md["authorization"]) -} - -func TestStaticToken_RequireTransportSecurity(t *testing.T) { - auth := StaticToken("token") - assert.True(t, auth.RequireTransportSecurity()) -} diff --git a/sdk/go/openshell/v1/client.go b/sdk/go/openshell/v1/client.go deleted file mode 100644 index 30e564f6c4..0000000000 --- a/sdk/go/openshell/v1/client.go +++ /dev/null @@ -1,171 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "sync" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - - internalgrpc "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/grpc" - "google.golang.org/grpc" -) - -// Config holds all settings needed to create a Client. -type Config = types.Config - -// ClientInterface defines the top-level SDK surface. -type ClientInterface interface { - Sandboxes() SandboxInterface - SandboxTemplates() SandboxTemplateInterface - CreateSandboxFromTemplate(ctx context.Context, workspace, name, templateName string, spec *SandboxSpec, opts ...CreateOption) (*Sandbox, error) - SandboxSuspendResume() SandboxSuspendResumeInterface - Providers() ProviderInterface - Services() ServiceInterface - Exec() ExecInterface - Files() FileInterface - Health() HealthInterface - SSH() SSHInterface - TCP() TCPInterface - Config() ConfigInterface - Policy() PolicyInterface - Workspaces() WorkspaceInterface - Close() error -} - -// SandboxInterface is defined in sandbox.go - -// ProviderInterface is defined in provider.go - -// ExecInterface is defined in exec.go - -// FileInterface is defined in file.go - -// Client implements ClientInterface. It holds a gRPC connection and provides -// sub-client accessors following the Kubernetes client-go pattern. -type Client struct { - conn *grpc.ClientConn - config Config - - closeOnce sync.Once - closeErr error - - sandboxes SandboxInterface - templateCreate SandboxTemplateCreateInterface - suspendResume SandboxSuspendResumeInterface - templates SandboxTemplateInterface - providers ProviderInterface - services ServiceInterface - exec ExecInterface - files FileInterface - health HealthInterface - ssh SSHInterface - tcp TCPInterface - cfg ConfigInterface - policy PolicyInterface - workspaces WorkspaceInterface -} - -// NewClient creates a new SDK client connected to the given gateway. -func NewClient(cfg Config) (*Client, error) { - if cfg.Address == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "address must not be empty"} - } - - if cfg.Auth == nil { - cfg.Auth = NoAuth() - } - - var tlsParams *internalgrpc.TLSParams - if cfg.TLS != nil { - tlsParams = &internalgrpc.TLSParams{ - CertFile: cfg.TLS.CertFile, - KeyFile: cfg.TLS.KeyFile, - CAFile: cfg.TLS.CAFile, - Insecure: cfg.TLS.Insecure, - } - } - - conn, err := internalgrpc.NewConnection(cfg.Address, tlsParams, cfg.Auth) - if err != nil { - return nil, err - } - - c := &Client{ - conn: conn, - config: cfg, - } - - sandboxes := newSandboxClient(conn) - c.sandboxes = sandboxes - c.templateCreate = sandboxes - c.suspendResume = sandboxes - c.templates = newSandboxTemplateClient(conn) - c.providers = newProviderClient(conn) - c.services = newServiceClient(conn) - c.exec = newExecClient(conn, c.sandboxes) - c.files = newFileClient(conn, c.sandboxes) - c.health = newHealthClient(conn) - c.ssh = newSSHClient(conn, c.sandboxes) - c.tcp = newTCPClient(conn, c.sandboxes, c.ssh) - c.cfg = newConfigClient(conn, c.sandboxes) - c.policy = newPolicyClient(conn) - c.workspaces = newWorkspaceClient(conn) - - return c, nil -} - -// Sandboxes returns the sandbox sub-client. -func (c *Client) Sandboxes() SandboxInterface { return c.sandboxes } - -// SandboxTemplates returns the reusable sandbox template sub-client. -func (c *Client) SandboxTemplates() SandboxTemplateInterface { return c.templates } - -// CreateSandboxFromTemplate creates a sandbox from a named workload template -// without changing the legacy Sandboxes() interface. -func (c *Client) CreateSandboxFromTemplate(ctx context.Context, workspace, name, templateName string, spec *SandboxSpec, opts ...CreateOption) (*Sandbox, error) { - return c.templateCreate.CreateFromTemplate(ctx, workspace, name, templateName, spec, opts...) -} - -// SandboxSuspendResume returns the sandbox suspend and resume sub-client. -func (c *Client) SandboxSuspendResume() SandboxSuspendResumeInterface { return c.suspendResume } - -// Providers returns the provider sub-client. -func (c *Client) Providers() ProviderInterface { return c.providers } - -// Services returns the service sub-client. -func (c *Client) Services() ServiceInterface { return c.services } - -// Exec returns the exec sub-client. -func (c *Client) Exec() ExecInterface { return c.exec } - -// Files returns the file sub-client. -func (c *Client) Files() FileInterface { return c.files } - -// Health returns the health sub-client. -func (c *Client) Health() HealthInterface { return c.health } - -// SSH returns the SSH session sub-client. -func (c *Client) SSH() SSHInterface { return c.ssh } - -// TCP returns the TCP port forwarding sub-client. -func (c *Client) TCP() TCPInterface { return c.tcp } - -// Config returns the configuration sub-client. -func (c *Client) Config() ConfigInterface { return c.cfg } - -// Policy returns the policy management sub-client. -func (c *Client) Policy() PolicyInterface { return c.policy } - -// Workspaces returns the workspace management sub-client. -func (c *Client) Workspaces() WorkspaceInterface { return c.workspaces } - -// Close closes the underlying gRPC connection. Safe to call multiple times. -func (c *Client) Close() error { - c.closeOnce.Do(func() { - c.closeErr = c.conn.Close() - }) - return c.closeErr -} diff --git a/sdk/go/openshell/v1/client_test.go b/sdk/go/openshell/v1/client_test.go deleted file mode 100644 index 47c886a8e1..0000000000 --- a/sdk/go/openshell/v1/client_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestNewClient_EmptyAddress(t *testing.T) { - _, err := NewClient(Config{Address: ""}) - require.Error(t, err) - assert.Contains(t, err.Error(), "address") -} - -func TestNewClient_ValidConfig(t *testing.T) { - client, err := NewClient(Config{ - Address: "localhost:50051", - Auth: NoAuth(), - TLS: &TLSConfig{Insecure: true}, - }) - require.NoError(t, err) - require.NotNil(t, client) - - assert.NotNil(t, client.Sandboxes()) - assert.NotNil(t, client.Providers()) - assert.NotNil(t, client.Exec()) - assert.NotNil(t, client.Files()) - assert.NotNil(t, client.Health()) - assert.NotNil(t, client.Services()) - assert.NotNil(t, client.SSH()) - assert.NotNil(t, client.TCP()) - assert.NotNil(t, client.Config()) - assert.NotNil(t, client.Policy()) - assert.NotNil(t, client.Workspaces()) - - err = client.Close() - assert.NoError(t, err) -} - -func TestClient_CloseIdempotent(t *testing.T) { - client, err := NewClient(Config{ - Address: "localhost:50051", - Auth: NoAuth(), - TLS: &TLSConfig{Insecure: true}, - }) - require.NoError(t, err) - - err = client.Close() - assert.NoError(t, err) - - err = client.Close() - assert.NoError(t, err) -} - -func TestNewClient_DefaultAuth(t *testing.T) { - client, err := NewClient(Config{ - Address: "localhost:50051", - TLS: &TLSConfig{Insecure: true}, - }) - require.NoError(t, err) - require.NotNil(t, client) - _ = client.Close() -} diff --git a/sdk/go/openshell/v1/config.go b/sdk/go/openshell/v1/config.go deleted file mode 100644 index c135afc572..0000000000 --- a/sdk/go/openshell/v1/config.go +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// SandboxConfig represents the full configuration state of a sandbox. -type SandboxConfig = types.SandboxConfig - -// GatewayConfig represents gateway-global settings. -type GatewayConfig = types.GatewayConfig - -// ConfigUpdate represents a configuration mutation request. -type ConfigUpdate = types.ConfigUpdate - -// ConfigUpdateResult holds the result of a configuration update operation. -type ConfigUpdateResult = types.ConfigUpdateResult - -// SettingValue is a typed setting value (string, bool, int64, or bytes). -type SettingValue = types.SettingValue - -// SettingValueType identifies which typed field of a SettingValue is active. -type SettingValueType = types.SettingValueType - -// EffectiveSetting is a setting value paired with its resolved scope. -type EffectiveSetting = types.EffectiveSetting - -// SettingScope indicates whether a setting is sandbox or global. -type SettingScope = types.SettingScope - -// PolicySource indicates the source of a policy payload. -type PolicySource = types.PolicySource - -// SettingValueType constants re-exported from types package. -const ( - SettingValueString = types.SettingValueString - SettingValueBool = types.SettingValueBool - SettingValueInt = types.SettingValueInt - SettingValueBytes = types.SettingValueBytes -) - -// SettingScope constants re-exported from types package. -const ( - SettingScopeUnspecified = types.SettingScopeUnspecified - SettingScopeSandbox = types.SettingScopeSandbox - SettingScopeGlobal = types.SettingScopeGlobal -) - -// PolicySource constants re-exported from types package. -const ( - PolicySourceUnspecified = types.PolicySourceUnspecified - PolicySourceSandbox = types.PolicySourceSandbox - PolicySourceGlobal = types.PolicySourceGlobal -) - -// ConfigInterface defines operations for reading and updating gateway and -// sandbox configuration. -type ConfigInterface interface { - GetSandbox(ctx context.Context, workspace, sandboxName string) (*SandboxConfig, error) - GetGateway(ctx context.Context) (*GatewayConfig, error) - Update(ctx context.Context, workspace string, update *ConfigUpdate) (*ConfigUpdateResult, error) -} diff --git a/sdk/go/openshell/v1/config_client.go b/sdk/go/openshell/v1/config_client.go deleted file mode 100644 index c8f7763344..0000000000 --- a/sdk/go/openshell/v1/config_client.go +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - sbv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - "google.golang.org/grpc" -) - -type configClient struct { - client pb.OpenShellClient - sandboxes SandboxInterface -} - -func newConfigClient(conn grpc.ClientConnInterface, sandboxes SandboxInterface) *configClient { - return &configClient{client: pb.NewOpenShellClient(conn), sandboxes: sandboxes} -} - -func (c *configClient) GetSandbox(ctx context.Context, workspace, sandboxName string) (*SandboxConfig, error) { - if sandboxName == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - if _, err := c.sandboxes.Get(ctx, workspace, sandboxName); err != nil { - return nil, err - } - resp, err := c.client.GetSandboxConfig(ctx, &sbv1.GetSandboxConfigRequest{ - Name: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.SandboxConfigFromProto(resp), nil -} - -func (c *configClient) GetGateway(ctx context.Context) (*GatewayConfig, error) { - resp, err := c.client.GetGatewayConfig(ctx, &sbv1.GetGatewayConfigRequest{}) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.GatewayConfigFromProto(resp), nil -} - -func (c *configClient) Update(ctx context.Context, workspace string, update *ConfigUpdate) (*ConfigUpdateResult, error) { - if update == nil { - return nil, &StatusError{ - Code: ErrorInvalidArgument, - Message: "update must not be nil", - } - } - req, convErr := converter.ConfigUpdateToProto(update) - if convErr != nil { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: convErr.Error()} - } - if !req.GetGlobal() { - req.WorkspaceScope = namedWorkspaceScope(workspace) - } - resp, err := c.client.UpdateConfig(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.ConfigUpdateResultFromProto(resp), nil -} diff --git a/sdk/go/openshell/v1/config_client_test.go b/sdk/go/openshell/v1/config_client_test.go deleted file mode 100644 index ab915ae7a3..0000000000 --- a/sdk/go/openshell/v1/config_client_test.go +++ /dev/null @@ -1,645 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "net" - "sync" - "testing" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - sbv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" -) - -// --- Mock server for Config RPCs --- - -type mockConfigServer struct { - pb.UnimplementedOpenShellServer - mu sync.Mutex - - // Canned responses. - sandboxResp *sbv1.GetSandboxConfigResponse - gatewayResp *sbv1.GetGatewayConfigResponse - updateResp *pb.UpdateConfigResponse - - // Recorded requests. - lastSandboxReq *sbv1.GetSandboxConfigRequest - lastGatewayReq *sbv1.GetGatewayConfigRequest - lastUpdateReq *pb.UpdateConfigRequest - - // Inject errors. - sandboxErr error - gatewayErr error - updateErr error -} - -func newMockConfigServer() *mockConfigServer { - return &mockConfigServer{} -} - -func (s *mockConfigServer) GetSandboxConfig(_ context.Context, req *sbv1.GetSandboxConfigRequest) (*sbv1.GetSandboxConfigResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastSandboxReq = req - if s.sandboxErr != nil { - return nil, s.sandboxErr - } - return s.sandboxResp, nil -} - -func (s *mockConfigServer) GetGatewayConfig(_ context.Context, req *sbv1.GetGatewayConfigRequest) (*sbv1.GetGatewayConfigResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastGatewayReq = req - if s.gatewayErr != nil { - return nil, s.gatewayErr - } - return s.gatewayResp, nil -} - -func (s *mockConfigServer) UpdateConfig(_ context.Context, req *pb.UpdateConfigRequest) (*pb.UpdateConfigResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastUpdateReq = req - if s.updateErr != nil { - return nil, s.updateErr - } - return s.updateResp, nil -} - -// --- Test setup --- - -func setupConfigTest(t *testing.T, mock *mockConfigServer) (*configClient, func()) { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - - return newConfigClient(conn, &stubSandboxResolver{}), func() { - _ = conn.Close() - srv.Stop() - } -} - -// --- GetSandbox tests --- - -func TestConfigGetSandbox(t *testing.T) { - mock := newMockConfigServer() - mock.sandboxResp = &sbv1.GetSandboxConfigResponse{ - Policy: &sbv1.SandboxPolicy{ - Version: 4, - Filesystem: &sbv1.FilesystemPolicy{ - ReadOnly: []string{"/etc"}, - }, - }, - Version: 3, - PolicyHash: "sha256:deadbeef", - ConfigRevision: 42, - PolicySource: sbv1.PolicySource_POLICY_SOURCE_SANDBOX, - GlobalPolicyVersion: 1, - ProviderEnvRevision: 7, - Settings: map[string]*sbv1.EffectiveSetting{ - "max_tokens": { - Value: &sbv1.SettingValue{ - Value: &sbv1.SettingValue_IntValue{IntValue: 4096}, - }, - Scope: sbv1.SettingScope_SETTING_SCOPE_SANDBOX, - }, - "debug": { - Value: &sbv1.SettingValue{ - Value: &sbv1.SettingValue_BoolValue{BoolValue: true}, - }, - Scope: sbv1.SettingScope_SETTING_SCOPE_GLOBAL, - }, - }, - } - - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - sc, err := client.GetSandbox(context.Background(), "default", "my-sandbox") - - require.NoError(t, err) - require.NotNil(t, sc) - - // Verify request was forwarded with resolved ID (stubSandboxResolver returns "sb-"). - mock.mu.Lock() - assert.Equal(t, "my-sandbox", mock.lastSandboxReq.GetName()) - mock.mu.Unlock() - - // Scalar fields. - assert.Equal(t, uint32(3), sc.PolicyVersion) - assert.Equal(t, "sha256:deadbeef", sc.PolicyHash) - assert.Equal(t, uint64(42), sc.ConfigRevision) - assert.Equal(t, PolicySource("sandbox"), sc.PolicySource) - assert.Equal(t, uint32(1), sc.GlobalPolicyVersion) - assert.Equal(t, uint64(7), sc.ProviderEnvRevision) - - // Typed SandboxPolicy. - require.NotNil(t, sc.Policy) - assert.Equal(t, uint32(4), sc.Policy.Version) - require.NotNil(t, sc.Policy.Filesystem) - assert.Equal(t, []string{"/etc"}, sc.Policy.Filesystem.ReadOnly) - - // Settings map. - require.Len(t, sc.Settings, 2) - - maxTok := sc.Settings["max_tokens"] - assert.Equal(t, SettingValueType("int"), maxTok.Value.Type) - assert.Equal(t, int64(4096), maxTok.Value.IntVal) - assert.Equal(t, SettingScope("sandbox"), maxTok.Scope) - - debug := sc.Settings["debug"] - assert.Equal(t, SettingValueType("bool"), debug.Value.Type) - assert.True(t, debug.Value.BoolVal) - assert.Equal(t, SettingScope("global"), debug.Scope) -} - -func TestConfigGetSandbox_DeepCopy(t *testing.T) { - mock := newMockConfigServer() - mock.sandboxResp = &sbv1.GetSandboxConfigResponse{ - Version: 1, - Settings: map[string]*sbv1.EffectiveSetting{ - "key": { - Value: &sbv1.SettingValue{ - Value: &sbv1.SettingValue_BytesValue{BytesValue: []byte("original")}, - }, - Scope: sbv1.SettingScope_SETTING_SCOPE_SANDBOX, - }, - }, - } - - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - sc, err := client.GetSandbox(context.Background(), "default", "sb1") - require.NoError(t, err) - - // Mutate the returned setting — should not affect future calls. - sc.Settings["key"] = EffectiveSetting{} - - sc2, err := client.GetSandbox(context.Background(), "default", "sb1") - require.NoError(t, err) - - // The server still returns the original value — verifies we're not - // sharing references between calls. - require.Contains(t, sc2.Settings, "key") - assert.Equal(t, []byte("original"), sc2.Settings["key"].Value.BytesVal) -} - -func TestConfigGetSandbox_Error(t *testing.T) { - mock := newMockConfigServer() - mock.sandboxErr = status.Errorf(codes.Unavailable, "server unavailable") - - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - sc, err := client.GetSandbox(context.Background(), "default", "my-sandbox") - - assert.Nil(t, sc) - require.Error(t, err) - assert.True(t, IsUnavailable(err)) -} - -// --- Name-to-ID resolution tests --- - -func TestConfigGetSandbox_UsesName(t *testing.T) { - mock := newMockConfigServer() - mock.sandboxResp = &sbv1.GetSandboxConfigResponse{Version: 1} - - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - sc, err := client.GetSandbox(context.Background(), "default", "my-sandbox") - require.NoError(t, err) - require.NotNil(t, sc) - - // stubSandboxResolver returns ID "sb-" — verify the proto has the resolved ID, not the name. - mock.mu.Lock() - assert.Equal(t, "my-sandbox", mock.lastSandboxReq.GetName()) - mock.mu.Unlock() -} - -func TestConfigGetSandbox_ResolutionError(t *testing.T) { - mock := newMockConfigServer() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - defer func() { - _ = conn.Close() - srv.Stop() - }() - - resolver := &stubSandboxResolver{ - getErr: &StatusError{Code: ErrorNotFound, Message: "sandbox not found"}, - } - client := newConfigClient(conn, resolver) - - sc, err := client.GetSandbox(context.Background(), "default", "nonexistent") - assert.Nil(t, sc) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -// --- GetGateway tests --- - -func TestConfigGetGateway(t *testing.T) { - mock := newMockConfigServer() - mock.gatewayResp = &sbv1.GetGatewayConfigResponse{ - SettingsRevision: 99, - Settings: map[string]*sbv1.SettingValue{ - "rate_limit": { - Value: &sbv1.SettingValue_IntValue{IntValue: 1000}, - }, - "motd": { - Value: &sbv1.SettingValue_StringValue{StringValue: "welcome"}, - }, - }, - } - - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - gc, err := client.GetGateway(context.Background()) - - require.NoError(t, err) - require.NotNil(t, gc) - - assert.Equal(t, uint64(99), gc.SettingsRevision) - require.Len(t, gc.Settings, 2) - - rl := gc.Settings["rate_limit"] - assert.Equal(t, SettingValueType("int"), rl.Type) - assert.Equal(t, int64(1000), rl.IntVal) - - motd := gc.Settings["motd"] - assert.Equal(t, SettingValueType("string"), motd.Type) - assert.Equal(t, "welcome", motd.StringVal) -} - -func TestConfigGetGateway_Error(t *testing.T) { - mock := newMockConfigServer() - mock.gatewayErr = status.Errorf(codes.Internal, "internal error") - - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - gc, err := client.GetGateway(context.Background()) - - assert.Nil(t, gc) - require.Error(t, err) - var se *StatusError - require.ErrorAs(t, err, &se) - assert.Equal(t, ErrorInternal, se.Code) -} - -// --- Update tests --- - -func TestConfigUpdate_SandboxScope(t *testing.T) { - mock := newMockConfigServer() - mock.updateResp = &pb.UpdateConfigResponse{ - Version: 5, - PolicyHash: "sha256:cafe", - SettingsRevision: 10, - Deleted: false, - } - - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - update := &ConfigUpdate{ - Name: "my-sandbox", - SettingKey: "max_tokens", - SettingValue: &SettingValue{ - Type: SettingValueInt, - IntVal: 8192, - }, - ExpectedResourceVersion: 4, - } - - result, err := client.Update(context.Background(), "default", update) - - require.NoError(t, err) - require.NotNil(t, result) - - assert.Equal(t, uint32(5), result.Version) - assert.Equal(t, "sha256:cafe", result.PolicyHash) - assert.Equal(t, uint64(10), result.SettingsRevision) - assert.False(t, result.Deleted) - - // Verify request was correctly converted. - mock.mu.Lock() - req := mock.lastUpdateReq - mock.mu.Unlock() - - require.NotNil(t, req) - assert.Equal(t, "my-sandbox", req.GetSandbox()) - assert.Equal(t, "max_tokens", req.GetSettingKey()) - assert.False(t, req.GetGlobal()) - assert.Equal(t, uint64(4), req.GetExpectedResourceVersion()) - assert.Equal(t, int64(8192), req.GetSettingValue().GetIntValue()) -} - -func TestConfigUpdate_GlobalScope(t *testing.T) { - mock := newMockConfigServer() - mock.updateResp = &pb.UpdateConfigResponse{ - SettingsRevision: 20, - } - - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - update := &ConfigUpdate{ - Global: true, - SettingKey: "global_flag", - SettingValue: &SettingValue{ - Type: SettingValueBool, - BoolVal: true, - }, - } - - result, err := client.Update(context.Background(), "default", update) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, uint64(20), result.SettingsRevision) - - mock.mu.Lock() - req := mock.lastUpdateReq - mock.mu.Unlock() - - assert.True(t, req.GetGlobal()) - assert.Empty(t, req.GetSandbox()) -} - -func TestConfigUpdate_DeleteSetting(t *testing.T) { - mock := newMockConfigServer() - mock.updateResp = &pb.UpdateConfigResponse{ - SettingsRevision: 15, - Deleted: true, - } - - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - update := &ConfigUpdate{ - Name: "my-sandbox", - SettingKey: "deprecated_key", - DeleteSetting: true, - } - - result, err := client.Update(context.Background(), "default", update) - - require.NoError(t, err) - require.NotNil(t, result) - assert.True(t, result.Deleted) - assert.Equal(t, uint64(15), result.SettingsRevision) - - mock.mu.Lock() - req := mock.lastUpdateReq - mock.mu.Unlock() - - assert.True(t, req.GetDeleteSetting()) -} - -func TestConfigUpdate_WithPolicy(t *testing.T) { - mock := newMockConfigServer() - mock.updateResp = &pb.UpdateConfigResponse{ - Version: 2, - PolicyHash: "sha256:newpolicy", - } - - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - update := &ConfigUpdate{ - Name: "my-sandbox", - Policy: &types.SandboxPolicy{ - Version: 5, - Filesystem: &types.FilesystemPolicy{ - ReadOnly: []string{"/usr"}, - }, - }, - } - - result, err := client.Update(context.Background(), "default", update) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, uint32(2), result.Version) - - // Verify the typed policy was converted and sent as proto. - mock.mu.Lock() - req := mock.lastUpdateReq - mock.mu.Unlock() - - require.NotNil(t, req.GetPolicy()) - assert.Equal(t, uint32(5), req.GetPolicy().GetVersion()) - require.NotNil(t, req.GetPolicy().GetFilesystem()) - assert.Equal(t, []string{"/usr"}, req.GetPolicy().GetFilesystem().GetReadOnly()) -} - -func TestConfigUpdate_RejectsUnrepresentableMiddlewareConfigBeforeRPC(t *testing.T) { - mock := newMockConfigServer() - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - _, err := client.Update(context.Background(), "default", &ConfigUpdate{Policy: &SandboxPolicy{ - NetworkMiddlewares: map[string]types.NetworkMiddlewareConfig{ - "audit": {Config: map[string]any{"invalid": make(chan int)}}, - }, - }}) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) - mock.mu.Lock() - defer mock.mu.Unlock() - assert.Nil(t, mock.lastUpdateReq) -} - -func TestConfigUpdate_Error(t *testing.T) { - mock := newMockConfigServer() - mock.updateErr = status.Errorf(codes.FailedPrecondition, "version mismatch") - - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - update := &ConfigUpdate{ - Name: "my-sandbox", - SettingKey: "key", - ExpectedResourceVersion: 99, - } - - result, err := client.Update(context.Background(), "default", update) - - assert.Nil(t, result) - require.Error(t, err) - var se *StatusError - require.ErrorAs(t, err, &se) - assert.Equal(t, ErrorConflict, se.Code) -} - -func TestConfigUpdate_NilUpdate(t *testing.T) { - mock := newMockConfigServer() - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - result, err := client.Update(context.Background(), "default", nil) - - assert.Nil(t, result) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestConfigUpdate_MergeOperationsAccepted(t *testing.T) { - mock := newMockConfigServer() - mock.updateResp = &pb.UpdateConfigResponse{ - Version: 3, - PolicyHash: "abc123", - } - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - update := &ConfigUpdate{ - Name: "my-sandbox", - MergeOperations: []types.PolicyMergeOperation{{RemoveRule: &types.RemoveNetworkRule{RuleName: "test"}}}, - } - - result, err := client.Update(context.Background(), "default", update) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, uint32(3), result.Version) - assert.Equal(t, "abc123", result.PolicyHash) - - // Verify the merge operations were serialized in the proto request - mock.mu.Lock() - req := mock.lastUpdateReq - mock.mu.Unlock() - require.NotNil(t, req) - assert.NotEmpty(t, req.GetMergeOperations(), "MergeOperations should be serialized to proto") -} - -func TestConfigUpdate_L7TargetScopeSurvivesTransport(t *testing.T) { - mock := newMockConfigServer() - mock.updateResp = &pb.UpdateConfigResponse{Version: 3} - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - path := "" - _, err := client.Update(context.Background(), "default", &ConfigUpdate{ - Name: "my-sandbox", - MergeOperations: []PolicyMergeOperation{ - {AddAllowRules: &AddAllowRules{ - Target: &L7RuleTarget{ - RuleName: "api", Host: "api.example.com", - Ports: []uint32{443, 8443}, - Path: &path, - Binaries: []PolicyNetworkBinary{{Path: "/usr/bin/curl"}, {Path: "/usr/bin/wget"}}, - }, - Rules: []L7Rule{{Allow: &L7Allow{Method: "POST", Path: "/admin"}}}, - }}, - {AddDenyRules: &AddDenyRules{ - Target: &L7RuleTarget{ - RuleName: "public-api", Host: "public.example.com", - Ports: []uint32{443}, - AnyBinary: true, - }, - DenyRules: []L7DenyRule{{Method: "DELETE", Path: "/admin"}}, - }}, - }, - }) - require.NoError(t, err) - - mock.mu.Lock() - req := mock.lastUpdateReq - mock.mu.Unlock() - require.NotNil(t, req) - require.Len(t, req.GetMergeOperations(), 2) - allow := req.GetMergeOperations()[0].GetAddAllowRules() - require.NotNil(t, allow) - allowTarget := allow.GetTarget() - require.NotNil(t, allowTarget) - assert.Equal(t, "api", allowTarget.GetRuleName()) - assert.Equal(t, "api.example.com", allowTarget.GetHost()) - assert.Equal(t, []uint32{443, 8443}, allowTarget.GetPorts()) - require.NotNil(t, allowTarget.Path, "empty endpoint path must survive protobuf encoding") - assert.Empty(t, *allowTarget.Path) - require.Len(t, allowTarget.GetBinaries(), 2) - assert.Equal(t, "/usr/bin/curl", allowTarget.GetBinaries()[0].GetPath()) - assert.Equal(t, "/usr/bin/wget", allowTarget.GetBinaries()[1].GetPath()) - assert.False(t, allowTarget.GetAnyBinary()) - require.Len(t, allow.GetRules(), 1) - assert.Equal(t, "POST", allow.GetRules()[0].GetAllow().GetMethod()) - assert.Equal(t, "/admin", allow.GetRules()[0].GetAllow().GetPath()) - - deny := req.GetMergeOperations()[1].GetAddDenyRules() - require.NotNil(t, deny) - denyTarget := deny.GetTarget() - require.NotNil(t, denyTarget) - assert.Equal(t, "public-api", denyTarget.GetRuleName()) - assert.Equal(t, "public.example.com", denyTarget.GetHost()) - assert.Equal(t, []uint32{443}, denyTarget.GetPorts()) - assert.Nil(t, denyTarget.Path) - assert.Empty(t, denyTarget.GetBinaries()) - assert.True(t, denyTarget.GetAnyBinary()) - require.Len(t, deny.GetDenyRules(), 1) - assert.Equal(t, "DELETE", deny.GetDenyRules()[0].GetMethod()) - assert.Equal(t, "/admin", deny.GetDenyRules()[0].GetPath()) -} - -func TestConfigUpdate_ErrorConflict(t *testing.T) { - mock := newMockConfigServer() - mock.updateErr = status.Error(codes.Aborted, "resource version conflict") - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - update := &ConfigUpdate{ - Name: "my-sandbox", - ExpectedResourceVersion: 5, - } - - result, err := client.Update(context.Background(), "default", update) - - assert.Nil(t, result) - require.Error(t, err) - assert.True(t, IsConflict(err)) -} - -func TestConfigGetSandbox_EmptySandboxName(t *testing.T) { - mock := newMockConfigServer() - client, cleanup := setupConfigTest(t, mock) - defer cleanup() - - cfg, err := client.GetSandbox(context.Background(), "default", "") - assert.Nil(t, cfg) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} diff --git a/sdk/go/openshell/v1/context_errors.go b/sdk/go/openshell/v1/context_errors.go deleted file mode 100644 index 3c934b3bac..0000000000 --- a/sdk/go/openshell/v1/context_errors.go +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import "context" - -func contextError(err error) error { - if err == nil { - return nil - } - switch err { - case context.DeadlineExceeded: - return &StatusError{Code: ErrorDeadlineExceeded, Message: err.Error(), Cause: err} - case context.Canceled: - return &StatusError{Code: ErrorCancelled, Message: err.Error(), Cause: err} - default: - return &StatusError{Code: ErrorInternal, Message: err.Error(), Cause: err} - } -} diff --git a/sdk/go/openshell/v1/context_errors_test.go b/sdk/go/openshell/v1/context_errors_test.go deleted file mode 100644 index 442a3ee32f..0000000000 --- a/sdk/go/openshell/v1/context_errors_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "errors" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestContextError_Nil(t *testing.T) { - result := contextError(nil) - assert.Nil(t, result) -} - -func TestContextError_DeadlineExceeded(t *testing.T) { - result := contextError(context.DeadlineExceeded) - - require.Error(t, result) - var se *StatusError - require.True(t, errors.As(result, &se)) - assert.Equal(t, ErrorDeadlineExceeded, se.Code) - assert.True(t, errors.Is(result, context.DeadlineExceeded)) -} - -func TestContextError_Canceled(t *testing.T) { - result := contextError(context.Canceled) - - require.Error(t, result) - var se *StatusError - require.True(t, errors.As(result, &se)) - assert.Equal(t, ErrorCancelled, se.Code) - assert.True(t, errors.Is(result, context.Canceled)) -} - -func TestContextError_Default(t *testing.T) { - orig := errors.New("unexpected context error") - result := contextError(orig) - - require.Error(t, result) - var se *StatusError - require.True(t, errors.As(result, &se)) - assert.Equal(t, ErrorInternal, se.Code) - assert.True(t, errors.Is(result, orig)) -} diff --git a/sdk/go/openshell/v1/doc.go b/sdk/go/openshell/v1/doc.go deleted file mode 100644 index a086bbbbe5..0000000000 --- a/sdk/go/openshell/v1/doc.go +++ /dev/null @@ -1,466 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package v1 provides a Go SDK for interacting with OpenShell servers. -// -// The SDK follows the Kubernetes client-go sub-client pattern: a single Client -// provides typed accessors for each resource domain (Sandboxes, Providers, Exec, -// Files, Health, Services, SSH, TCP, Config, Policy, and Workspaces). Network -// operations accept a context.Context and return idiomatic Go types. -// Proto-generated types never appear in the public API. -// -// # Quick Start -// -// 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() -// -// # Sandbox Lifecycle -// -// sandbox, err := client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{ -// Template: &v1.SandboxTemplate{Image: "python:3.12"}, -// Environment: map[string]string{"LANG": "en_US.UTF-8"}, -// }) -// if err != nil { -// log.Fatal(err) -// } -// -// sandbox, err = client.Sandboxes().WaitReady(ctx, "default", sandbox.Name) -// if err != nil { -// log.Fatal(err) -// } -// -// # Pagination -// -// List methods construct a lazy Pager without issuing an RPC. Each NextPage -// call fetches one page; ListAll is the explicit exhaustive convenience. -// -// pages, err := client.Sandboxes().List("default", v1.ListOptions{PageSize: 100}) -// if err != nil { -// log.Fatal(err) -// } -// for { -// page, err := pages.NextPage(ctx) -// if err != nil { -// log.Fatal(err) -// } -// if page == nil { -// break -// } -// for _, sandbox := range page.Items { -// fmt.Println(sandbox.Name) -// } -// } -// -// # Command Execution -// -// result, err := client.Exec().Run(ctx, "default", sandbox.Name, []string{"echo", "hello"}, v1.ExecOptions{}) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Println(string(result.Stdout)) // "hello\n" -// -// # Error Handling -// -// _, err = client.Sandboxes().Get(ctx, "default", "missing") -// if v1.IsNotFound(err) { -// // handle not found -// } -// -// # Watching -// -// watcher, err := client.Sandboxes().Watch(ctx, "default", sandbox.Name) -// if err != nil { -// log.Fatal(err) -// } -// defer watcher.Stop() -// for event := range watcher.ResultChan() { -// fmt.Printf("%s: %s\n", event.Type, event.Object.Name) -// } -// -// # Watching with StopOnTerminal -// -// Use StopOnTerminal to auto-close the watcher when the sandbox reaches a -// terminal phase (Ready or Error): -// -// watcher, err := client.Sandboxes().Watch(ctx, "default", sandbox.Name, -// v1.WatchOptions{StopOnTerminal: true}, -// ) -// if err != nil { -// log.Fatal(err) -// } -// for event := range watcher.ResultChan() { -// fmt.Printf("phase: %s\n", event.Object.Status.Phase) -// } -// // channel closes automatically after Ready or Error -// -// # Service Exposure -// -// Expose an HTTP service running inside a sandbox and retrieve its public URL: -// -// endpoint, err := client.Services().Expose(ctx, "default", "my-sandbox", "api", 8080, true) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Service URL: %s\n", endpoint.URL) -// -// endpoints, err := client.Services().ListAll(ctx, "default", "my-sandbox") -// if err != nil { -// log.Fatal(err) -// } -// for _, ep := range endpoints { -// fmt.Printf(" %s → port %d (URL: %s)\n", ep.Name, ep.TargetPort, ep.URL) -// } -// -// # Provider Profiles -// -// List available provider profiles and import new ones: -// -// profiles, err := client.Providers().Profiles().ListAll(ctx, "default") -// if err != nil { -// log.Fatal(err) -// } -// for _, p := range profiles { -// fmt.Printf("%s (%s): %s\n", p.DisplayName, p.Category, p.Description) -// } -// -// result, err := client.Providers().Profiles().Import(ctx, "default", []v1.ProfileImportItem{ -// {Source: "openai-profile.yaml", Profile: v1.ProviderProfile{ -// DisplayName: "OpenAI", -// Category: v1.ProfileCategoryInference, -// }}, -// }) -// if err != nil { -// log.Fatal(err) -// } -// for _, d := range result.Diagnostics { -// fmt.Printf("[%s] %s: %s\n", d.Severity, d.Field, d.Message) -// } -// -// # Credential Refresh -// -// Configure gateway-owned credential refresh for a provider: -// -// status, err := client.Providers().Refresh().Configure(ctx, "default", &v1.RefreshConfig{ -// Provider: "openai", -// CredentialKey: "api-key", -// Strategy: v1.RefreshStrategyOAuth2ClientCredentials, -// Material: map[string]string{"client_id": "xxx", "client_secret": "yyy"}, -// }) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Refresh status: %s (next: %s)\n", status.Status, status.NextRefreshAt) -// -// # Token Refresh -// -// Use RefreshableToken for automatic OAuth2 token caching and refresh. -// Concurrent callers share a single refresh call: -// -// 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() -// -// # Extra Headers -// -// Use WithExtraHeaders to attach additional per-RPC headers to any auth -// provider. This is useful for edge proxies, API gateways, or any middleware -// that requires custom headers alongside standard authentication: -// -// base := v1.StaticToken("my-token") -// auth, err := v1.WithExtraHeaders(base, map[string]string{ -// "x-proxy-key": "proxy-secret", -// "x-tenant-id": "acme-corp", -// }) -// 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() -// -// Keys are normalized to lowercase (per HTTP/2 RFC 9113). On key collision, -// extra headers take precedence over base auth headers. Empty-string values -// are silently dropped. WithExtraHeaders composes with any AuthProvider, -// including RefreshableToken: -// -// tokenSource := oauth2Config.TokenSource(ctx, initialToken) -// refreshAuth, err := v1.RefreshableToken(tokenSource) -// if err != nil { -// log.Fatal(err) -// } -// auth, err := v1.WithExtraHeaders(refreshAuth, map[string]string{ -// "x-proxy-key": "proxy-secret", -// }) -// -// # SSH Session Management -// -// Create an SSH session for a sandbox by its canonical name in a workspace and -// use the returned connection details. -// -// session, err := client.SSH().CreateSession(ctx, "default", sandbox.Name) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("SSH to %s:%d (scheme: %s)\n", -// session.GatewayHost, session.GatewayPort, session.GatewayScheme) -// fmt.Printf("Host key: %s\n", session.HostKeyFingerprint) -// // Use session.Token to authenticate the SSH connection. -// -// deletion, err := client.SSH().RevokeSession(ctx, "default", session.Token) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Revocation outcome: %v\n", deletion.Outcome) -// -// # TCP Port Forwarding -// -// Forward a local connection to a port inside a sandbox: -// -// conn, err := client.TCP().Forward(ctx, "default", "my-sandbox", 5432) -// if err != nil { -// log.Fatal(err) -// } -// defer conn.Close() -// -// // conn implements io.ReadWriteCloser, use it like a net.Conn. -// _, err = conn.Write([]byte("PING\n")) -// if err != nil { -// log.Fatal(err) -// } -// buf := make([]byte, 1024) -// n, err := conn.Read(buf) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Response: %s\n", buf[:n]) -// -// Use WithForwardServiceID to tag the forwarding session with a service -// identifier for audit logging: -// -// conn, err := client.TCP().Forward(ctx, "default", "my-sandbox", 5432, -// v1.WithForwardServiceID("billing-db"), -// ) -// -// # SSH Tunneling -// -// Create an SSH tunnel to a sandbox port in a single call. Tunnel combines -// session creation, TCP forwarding with an SSH relay target, and automatic -// session cleanup into one operation: -// -// tunnel, err := client.SSH().Tunnel(ctx, "default", "my-sandbox", 22) -// if err != nil { -// log.Fatal(err) -// } -// defer tunnel.Close() -// -// // tunnel implements io.ReadWriteCloser. The underlying SSH session -// // is automatically revoked when Close is called. -// _, err = tunnel.Write([]byte("SSH-2.0-client\r\n")) -// if err != nil { -// log.Fatal(err) -// } -// buf := make([]byte, 256) -// n, err := tunnel.Read(buf) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Server banner: %s\n", buf[:n]) -// -// Use WithTunnelServiceID to associate a service identifier with the tunnel: -// -// tunnel, err := client.SSH().Tunnel(ctx, "default", "my-sandbox", 22, -// v1.WithTunnelServiceID("dev-ssh"), -// ) -// -// # Sandbox Policy -// -// Set an initial security policy when creating a sandbox: -// -// sandbox, err := client.Sandboxes().Create(ctx, "default", "secure-sandbox", &v1.SandboxSpec{ -// Template: &v1.SandboxTemplate{Image: "python:3.12"}, -// Policy: &v1.SandboxPolicy{ -// Version: 1, -// Filesystem: &v1.FilesystemPolicy{ -// IncludeWorkdir: true, -// ReadOnly: []string{"/usr", "/lib"}, -// }, -// Process: &v1.ProcessPolicy{ -// RunAsUser: "sandbox", -// RunAsGroup: "sandbox", -// }, -// NetworkPolicies: map[string]v1.NetworkPolicyRule{ -// "allow-api": { -// Name: "allow-api", -// Endpoints: []v1.PolicyNetworkEndpoint{ -// {Host: "api.example.com", Port: 443, Protocol: "tcp"}, -// }, -// }, -// }, -// }, -// }) -// -// Replace the full policy at runtime via configuration update: -// -// result, err := client.Config().Update(ctx, "default", &v1.ConfigUpdate{ -// Name: "secure-sandbox", -// Policy: &v1.SandboxPolicy{ -// Version: 2, -// NetworkPolicies: map[string]v1.NetworkPolicyRule{ -// "allow-all": {Name: "allow-all"}, -// }, -// }, -// }) -// -// Read a policy back from revision history: -// -// revisions, err := client.Policy().ListAll(ctx, "default", "my-sandbox") -// if err != nil { -// log.Fatal(err) -// } -// for _, rev := range revisions { -// if rev.Policy != nil { -// fmt.Printf("v%d: %d network rules\n", rev.Version, len(rev.Policy.NetworkPolicies)) -// } -// } -// -// # Global Policy -// -// List gateway-global policy revisions (no sandbox name or workspace needed): -// -// revisions, err := client.Policy().ListAll(ctx, "", "", v1.WithListGlobal(true)) -// if err != nil { -// log.Fatal(err) -// } -// for _, rev := range revisions { -// fmt.Printf("Global v%d: %s\n", rev.Version, rev.Status) -// } -// -// Get the status of a specific global policy version: -// -// status, err := client.Policy().GetStatus(ctx, "", "", -// v1.WithStatusGlobal(true), v1.WithVersion(3), -// ) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Version %d status: %s\n", status.Revision.Version, status.Revision.Status) -// -// # Workspace Management -// -// Create and manage workspaces for multi-tenant resource isolation: -// -// ws, err := client.Workspaces().Create(ctx, "team-alpha", map[string]string{ -// "team": "alpha", -// "env": "production", -// }) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Workspace %s created (phase: %s)\n", ws.Name, ws.Phase) -// -// workspaces, err := client.Workspaces().ListAll(ctx) -// if err != nil { -// log.Fatal(err) -// } -// for _, w := range workspaces { -// fmt.Printf(" %s (phase: %s)\n", w.Name, w.Phase) -// } -// -// # Workspace Members -// -// Manage workspace membership with role-based access: -// -// member, err := client.Workspaces().AddMember(ctx, "team-alpha", -// "alice@example.com", v1.WorkspaceRoleAdmin) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Added %s as %s\n", member.PrincipalSubject, member.Role) -// -// members, err := client.Workspaces().ListAllMembers(ctx, "team-alpha") -// if err != nil { -// log.Fatal(err) -// } -// for _, m := range members { -// fmt.Printf(" %s (%s)\n", m.PrincipalSubject, m.Role) -// } -// -// # Gateway Info -// -// Query gateway metadata and compute driver capabilities: -// -// info, err := client.Health().GetGatewayInfo(ctx) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Gateway %s (status: %s)\n", info.Version, info.Status) -// for _, d := range info.ComputeDrivers { -// fmt.Printf(" Driver: %s %s\n", d.DriverName, d.DriverVersion) -// } -// -// # Current User -// -// Determine the identity of the authenticated caller: -// -// user, err := client.Health().GetCurrentUser(ctx) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Logged in as %s (%s)\n", user.DisplayName, user.Subject) -// fmt.Printf("Roles: %v, Scopes: %v\n", user.Roles, user.Scopes) -// -// # Configuration Management -// -// Read sandbox and gateway configuration, and update settings: -// -// sbCfg, err := client.Config().GetSandbox(ctx, "default", "my-sandbox") -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Config revision: %d\n", sbCfg.ConfigRevision) -// for name, setting := range sbCfg.Settings { -// fmt.Printf(" %s = %v (scope: %s)\n", name, setting.Value, setting.Scope) -// } -// -// gwCfg, err := client.Config().GetGateway(ctx) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Gateway settings revision: %d\n", gwCfg.SettingsRevision) -// -// result, err := client.Config().Update(ctx, "default", &v1.ConfigUpdate{ -// Name: "my-sandbox", -// SettingKey: "max_tokens", -// SettingValue: &v1.SettingValue{ -// Type: v1.SettingValueInt, -// IntVal: 8192, -// }, -// }) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("New settings revision: %d\n", result.SettingsRevision) -package v1 diff --git a/sdk/go/openshell/v1/edge/cloudflare.go b/sdk/go/openshell/v1/edge/cloudflare.go deleted file mode 100644 index 5363fe07f4..0000000000 --- a/sdk/go/openshell/v1/edge/cloudflare.go +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package edge - -import ( - "errors" - "fmt" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" -) - -// CloudflareAccess returns an AuthProvider that adds Cloudflare Access -// headers to every RPC. It sets: -// - cf-access-jwt-assertion: the edge JWT token -// - cookie: CF_Authorization= -// -// The edgeToken authenticates with the Cloudflare Access edge proxy. -// Returns an error if baseAuth is nil or edgeToken is empty. -func CloudflareAccess(baseAuth v1.AuthProvider, edgeToken string) (v1.AuthProvider, error) { - if edgeToken == "" { - return nil, errors.New("edge token must not be empty") - } - - return v1.WithExtraHeaders(baseAuth, map[string]string{ - "cf-access-jwt-assertion": edgeToken, - "cookie": fmt.Sprintf("CF_Authorization=%s", edgeToken), - }) -} diff --git a/sdk/go/openshell/v1/edge/cloudflare_test.go b/sdk/go/openshell/v1/edge/cloudflare_test.go deleted file mode 100644 index 902e599d69..0000000000 --- a/sdk/go/openshell/v1/edge/cloudflare_test.go +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package edge - -import ( - "context" - "testing" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestCloudflareAccess_ValidToken(t *testing.T) { - base := v1.StaticToken("my-token") - auth, err := CloudflareAccess(base, "cf-edge-jwt-xxx") - require.NoError(t, err) - - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - // Base auth header preserved. - assert.Equal(t, "Bearer my-token", md["authorization"]) - - // Cloudflare-specific headers present. - assert.Equal(t, "cf-edge-jwt-xxx", md["cf-access-jwt-assertion"]) - assert.Equal(t, "CF_Authorization=cf-edge-jwt-xxx", md["cookie"]) -} - -func TestCloudflareAccess_EmptyToken(t *testing.T) { - base := v1.StaticToken("my-token") - _, err := CloudflareAccess(base, "") - require.Error(t, err) - assert.Contains(t, err.Error(), "edge token") -} - -func TestCloudflareAccess_NilBase(t *testing.T) { - _, err := CloudflareAccess(nil, "cf-edge-jwt-xxx") - require.Error(t, err) - assert.Contains(t, err.Error(), "base") -} - -func TestCloudflareAccess_WithNoAuth(t *testing.T) { - auth, err := CloudflareAccess(v1.NoAuth(), "cf-edge-jwt-xxx") - require.NoError(t, err) - - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - - // NoAuth provides no base metadata; only CF headers should appear. - assert.Equal(t, "cf-edge-jwt-xxx", md["cf-access-jwt-assertion"]) - assert.Equal(t, "CF_Authorization=cf-edge-jwt-xxx", md["cookie"]) -} - -func TestCloudflareAccess_RequireTransportSecurity_Delegates(t *testing.T) { - tests := []struct { - name string - base v1.AuthProvider - expected bool - }{ - { - name: "delegates to NoAuth (false)", - base: v1.NoAuth(), - expected: false, - }, - { - name: "delegates to StaticToken (true)", - base: v1.StaticToken("tok"), - expected: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - auth, err := CloudflareAccess(tt.base, "cf-edge-jwt-xxx") - require.NoError(t, err) - assert.Equal(t, tt.expected, auth.RequireTransportSecurity()) - }) - } -} - -func TestCloudflareAccess_TokenNotInError(t *testing.T) { - // Verify the error for empty token does not leak actual token values. - _, err := CloudflareAccess(v1.StaticToken("s3cr3t-val"), "") - require.Error(t, err) - // The error should mention the parameter name, not any token value. - assert.NotContains(t, err.Error(), "s3cr3t-val") -} diff --git a/sdk/go/openshell/v1/edge/doc.go b/sdk/go/openshell/v1/edge/doc.go deleted file mode 100644 index 92fd9b1f9f..0000000000 --- a/sdk/go/openshell/v1/edge/doc.go +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package edge provides utilities for connecting to OpenShell gateways -// through edge proxies such as Cloudflare Access. It includes convenience -// constructors for common edge auth patterns and a WebSocket tunnel proxy -// for gRPC transport through HTTP/1.1-only proxies. -// -// # Cloudflare Access -// -// CloudflareAccess wraps any AuthProvider with the headers required by -// Cloudflare Access (cf-access-jwt-assertion and CF_Authorization cookie). -// The edge token is typically a service token or application token obtained -// from Cloudflare: -// -// base := v1.StaticToken("my-gateway-token") -// auth, err := edge.CloudflareAccess(base, os.Getenv("CF_ACCESS_TOKEN")) -// 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() -// -// CloudflareAccess composes with any auth provider, including RefreshableToken -// for automatic token refresh: -// -// tokenSource := oauth2Config.TokenSource(ctx, initialToken) -// refreshAuth, err := v1.RefreshableToken(tokenSource) -// if err != nil { -// log.Fatal(err) -// } -// auth, err := edge.CloudflareAccess(refreshAuth, cfToken) -// if err != nil { -// log.Fatal(err) -// } -// -// # WebSocket Tunnel -// -// TunnelProxy bridges gRPC connections over a WebSocket tunnel for edge -// proxies that reject standard HTTP/2 POST requests. The tunnel carries -// its own edge token for proxy authentication, independent of the -// application-level auth provider. -// -// Create a tunnel proxy pointed at the gateway, then dial the proxy's -// local address from the gRPC client: -// -// tunnel, err := edge.NewTunnelProxy( -// "wss://gateway.example.com/ws", -// os.Getenv("CF_ACCESS_TOKEN"), -// ) -// if err != nil { -// log.Fatal(err) -// } -// defer tunnel.Close() -// -// auth := v1.StaticToken("my-gateway-token") -// client, err := v1.NewClient(v1.Config{ -// Address: tunnel.Addr(), -// Auth: auth, -// TLS: &v1.TLSConfig{Insecure: true}, // local tunnel -// }) -// if err != nil { -// log.Fatal(err) -// } -// defer client.Close() -// -// Use functional options to configure TLS, logging, and close timeout: -// -// tunnel, err := edge.NewTunnelProxy( -// "wss://gateway.example.com/ws", -// cfToken, -// edge.WithTunnelTLS(&tls.Config{RootCAs: customCertPool}), -// edge.WithTunnelLogger(myLogger), -// edge.WithCloseTimeout(10*time.Second), -// ) -// -// Close drains in-flight connections gracefully. If draining exceeds the -// configured timeout (default 5 seconds), remaining connections are -// force-closed: -// -// err := tunnel.Close() // safe to call multiple times -package edge diff --git a/sdk/go/openshell/v1/edge/options_nil_test.go b/sdk/go/openshell/v1/edge/options_nil_test.go deleted file mode 100644 index 720e357131..0000000000 --- a/sdk/go/openshell/v1/edge/options_nil_test.go +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package edge - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" -) - -func TestTunnelOption_NilIgnored(t *testing.T) { - cfg := tunnelConfig{closeTimeout: defaultCloseTimeout} - options.Apply(&cfg, []TunnelOption{ - WithTunnelLogger(nil), - nil, - WithCloseTimeout(10 * time.Second), - }) - assert.Equal(t, 10*time.Second, cfg.closeTimeout) -} diff --git a/sdk/go/openshell/v1/edge/tunnel.go b/sdk/go/openshell/v1/edge/tunnel.go deleted file mode 100644 index 6576c9275a..0000000000 --- a/sdk/go/openshell/v1/edge/tunnel.go +++ /dev/null @@ -1,294 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package edge - -import ( - "context" - "crypto/tls" - "errors" - "fmt" - "io" - "net" - "net/http" - "net/url" - "sync" - "time" - - "github.com/coder/websocket" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -const defaultCloseTimeout = 5 * time.Second - -// tunnelConfig holds configuration set by TunnelOption functions. -type tunnelConfig struct { - logger types.Logger - tlsConfig *tls.Config - closeTimeout time.Duration -} - -// TunnelOption configures TunnelProxy behavior. -type TunnelOption func(*tunnelConfig) - -// WithTunnelLogger sets the structured logger for tunnel events. -func WithTunnelLogger(l types.Logger) TunnelOption { - return func(c *tunnelConfig) { - c.logger = l - } -} - -// WithTunnelTLS sets TLS configuration for the WebSocket connection (wss://). -func WithTunnelTLS(cfg *tls.Config) TunnelOption { - return func(c *tunnelConfig) { - c.tlsConfig = cfg - } -} - -// WithCloseTimeout sets the maximum time Close waits for in-flight -// connections to drain before force-closing. Default is 5 seconds. -func WithCloseTimeout(d time.Duration) TunnelOption { - return func(c *tunnelConfig) { - c.closeTimeout = d - } -} - -// TunnelProxy bridges gRPC connections over a WebSocket tunnel. -// The gRPC client dials TunnelProxy.Addr() instead of the remote gateway. -// Each accepted connection spawns a goroutine that dials the gateway over -// WebSocket and copies data bidirectionally. -type TunnelProxy struct { - listener net.Listener - gatewayURL string - edgeToken string - logger types.Logger - closeTimeout time.Duration - httpClient *http.Client - - ctx context.Context - cancel context.CancelFunc - wg sync.WaitGroup - mu sync.Mutex - closing bool - closeOnce sync.Once - closeErr error -} - -// NewTunnelProxy creates a tunnel proxy that forwards TCP connections -// through a WebSocket connection to gatewayURL. The edgeToken authenticates -// with the edge proxy via Cloudflare Access headers on the WebSocket -// handshake. -// -// Returns error if gatewayURL is empty or invalid, or if edgeToken is empty. -func NewTunnelProxy(gatewayURL, edgeToken string, opts ...TunnelOption) (*TunnelProxy, error) { - if gatewayURL == "" { - return nil, errors.New("gateway URL must not be empty") - } - if edgeToken == "" { - return nil, errors.New("edge token must not be empty") - } - - // Validate the URL parses correctly. - u, err := url.Parse(gatewayURL) - if err != nil { - return nil, fmt.Errorf("invalid gateway URL: %w", err) - } - if u.Scheme != "ws" && u.Scheme != "wss" { - return nil, fmt.Errorf("gateway URL must use ws:// or wss:// scheme, got %q", u.Scheme) - } - if u.Host == "" { - return nil, errors.New("gateway URL must include a host") - } - if u.Scheme == "ws" && !isLoopbackHost(u.Hostname()) { - return nil, errors.New("gateway URL with an edge token must use wss:// (ws:// is allowed only for loopback hosts)") - } - - cfg := tunnelConfig{ - closeTimeout: defaultCloseTimeout, - } - options.Apply(&cfg, opts) - - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - return nil, fmt.Errorf("listen: %w", err) - } - - ctx, cancel := context.WithCancel(context.Background()) - - var httpClient *http.Client - if cfg.tlsConfig != nil { - httpClient = &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: cfg.tlsConfig, - }, - } - } - - tp := &TunnelProxy{ - listener: listener, - gatewayURL: gatewayURL, - edgeToken: edgeToken, - logger: cfg.logger, - closeTimeout: cfg.closeTimeout, - httpClient: httpClient, - ctx: ctx, - cancel: cancel, - } - - // Start the accept loop. - tp.wg.Add(1) - go tp.acceptLoop() - - return tp, nil -} - -func isLoopbackHost(host string) bool { - if host == "localhost" { - return true - } - ip := net.ParseIP(host) - return ip != nil && ip.IsLoopback() -} - -// Addr returns the local address the gRPC client should dial. -func (tp *TunnelProxy) Addr() string { - return tp.listener.Addr().String() -} - -// Close drains in-flight connections (up to the configured timeout, -// default 5s) then force-closes any remaining connections. All goroutines -// are cleaned up. Safe to call multiple times; the second and subsequent -// calls return immediately. -func (tp *TunnelProxy) Close() error { - tp.closeOnce.Do(func() { - tp.mu.Lock() - tp.closing = true - tp.mu.Unlock() - - // Stop accepting new connections. - tp.closeErr = tp.listener.Close() - - // Wait for in-flight connections to drain, with a timeout. - done := make(chan struct{}) - go func() { - tp.wg.Wait() - close(done) - }() - - select { - case <-done: - // All goroutines drained cleanly. - case <-time.After(tp.closeTimeout): - // Timeout reached; cancel all bridge contexts to force-close. - if tp.logger != nil { - tp.logger.Info("tunnel close timeout reached, force-closing") - } - tp.cancel() - <-done - } - // Always cancel to release the context tree. - tp.cancel() - }) - return tp.closeErr -} - -// acceptLoop runs in a goroutine. It accepts local TCP connections and -// spawns a bridge goroutine for each one. -func (tp *TunnelProxy) acceptLoop() { - defer tp.wg.Done() - - for { - conn, err := tp.listener.Accept() - if err != nil { - if errors.Is(err, net.ErrClosed) { - return - } - if tp.logger != nil { - tp.logger.Error(err, "tunnel accept error") - } - time.Sleep(10 * time.Millisecond) - continue - } - - tp.mu.Lock() - if tp.closing { - tp.mu.Unlock() - _ = conn.Close() - return - } - tp.wg.Add(1) - tp.mu.Unlock() - - if tp.logger != nil { - tp.logger.Debug("tunnel connection accepted", "remote", conn.RemoteAddr().String()) - } - - go tp.bridge(conn) - } -} - -// bridge dials the gateway over WebSocket and copies data bidirectionally -// between the local TCP connection and the WebSocket connection. -func (tp *TunnelProxy) bridge(local net.Conn) { - defer tp.wg.Done() - defer func() { _ = local.Close() }() - - ctx, cancel := context.WithCancel(tp.ctx) - defer cancel() - - // Build WebSocket dial options with edge auth headers. - dialOpts := &websocket.DialOptions{ - HTTPHeader: http.Header{ - "cf-access-jwt-assertion": []string{tp.edgeToken}, - "cookie": []string{fmt.Sprintf("CF_Authorization=%s", tp.edgeToken)}, - }, - } - if tp.httpClient != nil { - dialOpts.HTTPClient = tp.httpClient - } - - dialCtx, dialCancel := context.WithTimeout(ctx, 10*time.Second) - defer dialCancel() - - wsConn, _, err := websocket.Dial(dialCtx, tp.gatewayURL, dialOpts) - if err != nil { - if tp.logger != nil { - tp.logger.Error(err, "tunnel websocket dial failed") - } - return - } - defer func() { _ = wsConn.CloseNow() }() - - // Set a generous read limit for gRPC frames. - wsConn.SetReadLimit(64 * 1024 * 1024) // 64 MiB - - // Convert the WebSocket connection to a net.Conn for bidirectional I/O. - remote := websocket.NetConn(ctx, wsConn, websocket.MessageBinary) - - // Bidirectional copy. - done := make(chan struct{}, 2) - - // Local -> Remote (WebSocket) - go func() { - _, _ = io.Copy(remote, local) - done <- struct{}{} - }() - - // Remote (WebSocket) -> Local - go func() { - _, _ = io.Copy(local, remote) - done <- struct{}{} - }() - - // Wait for one direction to finish, then tear down both. - <-done - cancel() - _ = local.Close() - <-done - - if tp.logger != nil { - tp.logger.Debug("tunnel bridge closed") - } -} diff --git a/sdk/go/openshell/v1/edge/tunnel_test.go b/sdk/go/openshell/v1/edge/tunnel_test.go deleted file mode 100644 index 4a2215e187..0000000000 --- a/sdk/go/openshell/v1/edge/tunnel_test.go +++ /dev/null @@ -1,500 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package edge - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "io" - "net" - "net/http" - "net/http/httptest" - "runtime" - "slices" - "sync" - "testing" - "time" - - "github.com/coder/websocket" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// echoWSHandler accepts a WebSocket connection and echoes every binary -// message back to the sender until the client disconnects. -func echoWSHandler(w http.ResponseWriter, r *http.Request) { - conn, err := websocket.Accept(w, r, &websocket.AcceptOptions{ - InsecureSkipVerify: true, - }) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - defer func() { _ = conn.CloseNow() }() - - ctx := r.Context() - for { - typ, data, err := conn.Read(ctx) - if err != nil { - return - } - if err := conn.Write(ctx, typ, data); err != nil { - return - } - } -} - -// startEchoServer starts an HTTP test server that upgrades connections to -// WebSocket and echoes binary messages. Returns the server and its ws:// URL. -func startEchoServer(t *testing.T) (*httptest.Server, string) { - t.Helper() - srv := httptest.NewServer(http.HandlerFunc(echoWSHandler)) - t.Cleanup(srv.Close) - // Convert http://host:port to ws://host:port. - wsURL := "ws" + srv.URL[len("http"):] - return srv, wsURL -} - -// startTLSEchoServer starts a TLS HTTP test server. Returns the server, -// its wss:// URL, and a tls.Config that trusts the server's certificate. -func startTLSEchoServer(t *testing.T) (*httptest.Server, string, *tls.Config) { - t.Helper() - srv := httptest.NewTLSServer(http.HandlerFunc(echoWSHandler)) - t.Cleanup(srv.Close) - wssURL := "wss" + srv.URL[len("https"):] - - certPool := x509.NewCertPool() - certPool.AddCert(srv.Certificate()) - tlsCfg := &tls.Config{ - RootCAs: certPool, - } - return srv, wssURL, tlsCfg -} - -// testLogger captures log messages for assertions. -type testLogger struct { - mu sync.Mutex - messages []string -} - -func (l *testLogger) Debug(msg string, _ ...any) { - l.mu.Lock() - defer l.mu.Unlock() - l.messages = append(l.messages, "DEBUG: "+msg) -} - -func (l *testLogger) Info(msg string, _ ...any) { - l.mu.Lock() - defer l.mu.Unlock() - l.messages = append(l.messages, "INFO: "+msg) -} - -func (l *testLogger) Error(_ error, msg string, _ ...any) { - l.mu.Lock() - defer l.mu.Unlock() - l.messages = append(l.messages, "ERROR: "+msg) -} - -func (l *testLogger) Messages() []string { - l.mu.Lock() - defer l.mu.Unlock() - cp := make([]string, len(l.messages)) - copy(cp, l.messages) - return cp -} - -// --- Test: NewTunnelProxy creation --- - -func TestNewTunnelProxy_ValidURL(t *testing.T) { - _, wsURL := startEchoServer(t) - - tp, err := NewTunnelProxy(wsURL, "edge-token-123") - require.NoError(t, err) - require.NotNil(t, tp) - defer func() { _ = tp.Close() }() - - // Addr() must return a non-empty, dialable address. - addr := tp.Addr() - assert.NotEmpty(t, addr) - - // Verify the address is dialable. - conn, err := net.DialTimeout("tcp", addr, 2*time.Second) - require.NoError(t, err) - _ = conn.Close() -} - -func TestNewTunnelProxy_EmptyURL(t *testing.T) { - _, err := NewTunnelProxy("", "edge-token-123") - require.Error(t, err) - assert.Contains(t, err.Error(), "gateway URL") -} - -func TestNewTunnelProxy_InvalidURL(t *testing.T) { - _, err := NewTunnelProxy("://bad-url", "edge-token-123") - require.Error(t, err) -} - -func TestNewTunnelProxy_WrongScheme(t *testing.T) { - _, err := NewTunnelProxy("http://gateway.example.com", "edge-token-123") - require.Error(t, err) - assert.Contains(t, err.Error(), "ws:// or wss://") -} - -func TestNewTunnelProxy_RejectsCredentialBearingRemoteWS(t *testing.T) { - _, err := NewTunnelProxy("ws://gateway.example.com/tunnel", "edge-token-123") - require.Error(t, err) - assert.Contains(t, err.Error(), "wss://") -} - -func TestNewTunnelProxy_EmptyHost(t *testing.T) { - _, err := NewTunnelProxy("ws://", "edge-token-123") - require.Error(t, err) - assert.Contains(t, err.Error(), "must include a host") -} - -func TestNewTunnelProxy_EmptyEdgeToken(t *testing.T) { - _, wsURL := startEchoServer(t) - - _, err := NewTunnelProxy(wsURL, "") - require.Error(t, err) - assert.Contains(t, err.Error(), "edge token") -} - -func TestNewTunnelProxy_TokenNotInError(t *testing.T) { - // Verify error messages do not leak the edge token. - _, err := NewTunnelProxy("", "super-secret-token-abc") - require.Error(t, err) - assert.NotContains(t, err.Error(), "super-secret-token-abc") -} - -// --- Test: Addr --- - -func TestTunnelProxy_Addr_Dialable(t *testing.T) { - _, wsURL := startEchoServer(t) - - tp, err := NewTunnelProxy(wsURL, "tok") - require.NoError(t, err) - defer func() { _ = tp.Close() }() - - addr := tp.Addr() - host, port, err := net.SplitHostPort(addr) - require.NoError(t, err) - assert.NotEmpty(t, host) - assert.NotEmpty(t, port) -} - -// --- Test: Close on unused proxy --- - -func TestTunnelProxy_Close_Unused(t *testing.T) { - _, wsURL := startEchoServer(t) - - tp, err := NewTunnelProxy(wsURL, "tok") - require.NoError(t, err) - - // Close immediately without any connections should return nil. - err = tp.Close() - assert.NoError(t, err) -} - -// --- Test: Close drains in-flight connections --- - -func TestTunnelProxy_Close_DrainsInFlight(t *testing.T) { - _, wsURL := startEchoServer(t) - - tp, err := NewTunnelProxy(wsURL, "tok", WithCloseTimeout(5*time.Second)) - require.NoError(t, err) - - // Establish a connection through the tunnel. - conn, err := net.DialTimeout("tcp", tp.Addr(), 2*time.Second) - require.NoError(t, err) - - // Send data through the tunnel and verify echo. - testData := []byte("hello tunnel") - _, err = conn.Write(testData) - require.NoError(t, err) - - // Give the tunnel time to relay. - time.Sleep(100 * time.Millisecond) - - // Close the client connection first so the bridge goroutine can drain. - _ = conn.Close() - - // Now close the tunnel; should drain cleanly. - err = tp.Close() - assert.NoError(t, err) -} - -// --- Test: Close force-closes after timeout --- - -func TestTunnelProxy_Close_ForceClosesAfterTimeout(t *testing.T) { - // Use a slow handler that holds connections open. - slowHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - wsConn, err := websocket.Accept(w, r, &websocket.AcceptOptions{ - InsecureSkipVerify: true, - }) - if err != nil { - return - } - defer func() { _ = wsConn.CloseNow() }() - // Hold the connection open for a long time. - ctx := r.Context() - select { - case <-ctx.Done(): - case <-time.After(30 * time.Second): - } - }) - srv := httptest.NewServer(slowHandler) - defer srv.Close() - wsURL := "ws" + srv.URL[len("http"):] - - // Use a very short close timeout and a logger to verify timeout logging. - logger := &testLogger{} - tp, err := NewTunnelProxy(wsURL, "tok", WithCloseTimeout(200*time.Millisecond), WithTunnelLogger(logger)) - require.NoError(t, err) - - // Establish a connection that will be held open by the slow handler. - conn, err := net.DialTimeout("tcp", tp.Addr(), 2*time.Second) - require.NoError(t, err) - defer func() { _ = conn.Close() }() - - // Write something to trigger the WebSocket dial. - _, _ = conn.Write([]byte("trigger")) - - // Give the tunnel time to establish the bridge. - time.Sleep(100 * time.Millisecond) - - // Close should force-close after the short timeout, not hang. - start := time.Now() - err = tp.Close() - elapsed := time.Since(start) - - // Should complete within a reasonable time (timeout + margin). - assert.Less(t, elapsed, 2*time.Second, "Close should not hang beyond timeout") - // err may or may not be nil depending on force-close; we don't assert on it. - _ = err - - // Verify the timeout was logged. - msgs := logger.Messages() - assert.True(t, slices.Contains(msgs, "INFO: tunnel close timeout reached, force-closing"), - "expected timeout log message, got: %v", msgs) -} - -// --- Test: Concurrent Close is safe --- - -func TestTunnelProxy_Close_ConcurrentSafe(t *testing.T) { - _, wsURL := startEchoServer(t) - - tp, err := NewTunnelProxy(wsURL, "tok") - require.NoError(t, err) - - // Call Close concurrently from multiple goroutines. - var wg sync.WaitGroup - errs := make([]error, 10) - for i := range errs { - wg.Add(1) - go func(idx int) { - defer wg.Done() - errs[idx] = tp.Close() - }(i) - } - wg.Wait() - - // All calls should succeed without panic. - for _, e := range errs { - assert.NoError(t, e) - } -} - -// --- Test: Goroutine cleanup --- - -func TestTunnelProxy_GoroutineCleanup(t *testing.T) { - _, wsURL := startEchoServer(t) - - // Record baseline goroutine count. - runtime.GC() - time.Sleep(50 * time.Millisecond) - baseline := runtime.NumGoroutine() - - tp, err := NewTunnelProxy(wsURL, "tok") - require.NoError(t, err) - - // Open several connections. - conns := make([]net.Conn, 5) - for i := range conns { - c, err := net.DialTimeout("tcp", tp.Addr(), 2*time.Second) - require.NoError(t, err) - _, _ = fmt.Fprintf(c, "msg-%d", i) - conns[i] = c - } - - // Let bridges establish. - time.Sleep(100 * time.Millisecond) - - // Close all client connections. - for _, c := range conns { - _ = c.Close() - } - - // Close the tunnel. - err = tp.Close() - require.NoError(t, err) - - // Wait for goroutines to wind down. - time.Sleep(200 * time.Millisecond) - runtime.GC() - - // Goroutine count should return to near baseline. - // Allow a small margin for runtime goroutines. - final := runtime.NumGoroutine() - assert.LessOrEqual(t, final, baseline+3, - "goroutine leak: baseline=%d, final=%d", baseline, final) -} - -// --- Test: Concurrent streams --- - -func TestTunnelProxy_ConcurrentStreams(t *testing.T) { - _, wsURL := startEchoServer(t) - - tp, err := NewTunnelProxy(wsURL, "tok") - require.NoError(t, err) - defer func() { _ = tp.Close() }() - - const streamCount = 10 - var wg sync.WaitGroup - errs := make(chan error, streamCount) - - for i := range streamCount { - wg.Add(1) - go func(idx int) { - defer wg.Done() - - conn, err := net.DialTimeout("tcp", tp.Addr(), 2*time.Second) - if err != nil { - errs <- fmt.Errorf("stream %d dial: %w", idx, err) - return - } - defer func() { _ = conn.Close() }() - - msg := fmt.Sprintf("stream-%d-data", idx) - _, err = conn.Write([]byte(msg)) - if err != nil { - errs <- fmt.Errorf("stream %d write: %w", idx, err) - return - } - - // Read echo response. - buf := make([]byte, len(msg)) - _ = conn.SetReadDeadline(time.Now().Add(3 * time.Second)) - n, err := io.ReadFull(conn, buf) - if err != nil { - errs <- fmt.Errorf("stream %d read: %w (got %d bytes)", idx, err, n) - return - } - - if string(buf) != msg { - errs <- fmt.Errorf("stream %d: expected %q, got %q", idx, msg, string(buf)) - } - }(i) - } - - wg.Wait() - close(errs) - - for err := range errs { - t.Error(err) - } -} - -// --- Test: TLS option --- - -func TestTunnelProxy_TLSOption(t *testing.T) { - _, wssURL, tlsCfg := startTLSEchoServer(t) - - tp, err := NewTunnelProxy(wssURL, "tok", WithTunnelTLS(tlsCfg)) - require.NoError(t, err) - defer func() { _ = tp.Close() }() - - // Verify we can communicate through the TLS tunnel. - conn, err := net.DialTimeout("tcp", tp.Addr(), 2*time.Second) - require.NoError(t, err) - defer func() { _ = conn.Close() }() - - msg := []byte("tls-echo-test") - _, err = conn.Write(msg) - require.NoError(t, err) - - buf := make([]byte, len(msg)) - _ = conn.SetReadDeadline(time.Now().Add(3 * time.Second)) - _, err = io.ReadFull(conn, buf) - require.NoError(t, err) - assert.Equal(t, msg, buf) -} - -// --- Test: Logger option --- - -func TestTunnelProxy_LoggerOption(t *testing.T) { - _, wsURL := startEchoServer(t) - - logger := &testLogger{} - tp, err := NewTunnelProxy(wsURL, "tok", WithTunnelLogger(logger)) - require.NoError(t, err) - - // Open a connection to trigger log events. - conn, err := net.DialTimeout("tcp", tp.Addr(), 2*time.Second) - require.NoError(t, err) - - _, _ = conn.Write([]byte("log-test")) - - // Give the tunnel time to process. - time.Sleep(200 * time.Millisecond) - - _ = conn.Close() - - err = tp.Close() - require.NoError(t, err) - - // Logger should have received at least one message. - msgs := logger.Messages() - assert.NotEmpty(t, msgs, "logger should receive log events") -} - -// --- Test: WithCloseTimeout option --- - -func TestWithCloseTimeout(t *testing.T) { - _, wsURL := startEchoServer(t) - - tp, err := NewTunnelProxy(wsURL, "tok", WithCloseTimeout(10*time.Second)) - require.NoError(t, err) - defer func() { _ = tp.Close() }() - - // We can't directly inspect the config, but creation should succeed. - assert.NotNil(t, tp) -} - -// --- Test: Data flows through the tunnel --- - -func TestTunnelProxy_DataRoundTrip(t *testing.T) { - _, wsURL := startEchoServer(t) - - tp, err := NewTunnelProxy(wsURL, "tok") - require.NoError(t, err) - defer func() { _ = tp.Close() }() - - conn, err := net.DialTimeout("tcp", tp.Addr(), 2*time.Second) - require.NoError(t, err) - defer func() { _ = conn.Close() }() - - // Send data and verify round-trip through the WebSocket echo server. - msg := []byte("round-trip-payload-12345") - _, err = conn.Write(msg) - require.NoError(t, err) - - buf := make([]byte, len(msg)) - _ = conn.SetReadDeadline(time.Now().Add(3 * time.Second)) - _, err = io.ReadFull(conn, buf) - require.NoError(t, err) - - assert.Equal(t, msg, buf) -} diff --git a/sdk/go/openshell/v1/errors.go b/sdk/go/openshell/v1/errors.go deleted file mode 100644 index ced7769bbe..0000000000 --- a/sdk/go/openshell/v1/errors.go +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// ErrorCode classifies SDK errors by their gRPC origin. -type ErrorCode = types.ErrorCode - -// ErrorCode values for classifying gRPC errors. -const ( - ErrorNotFound = types.ErrorNotFound - ErrorAlreadyExists = types.ErrorAlreadyExists - ErrorUnavailable = types.ErrorUnavailable - ErrorPermissionDenied = types.ErrorPermissionDenied - ErrorInvalidArgument = types.ErrorInvalidArgument - ErrorDeadlineExceeded = types.ErrorDeadlineExceeded - ErrorCancelled = types.ErrorCancelled - ErrorInternal = types.ErrorInternal - ErrorUnimplemented = types.ErrorUnimplemented - ErrorConflict = types.ErrorConflict - ErrorUnauthenticated = types.ErrorUnauthenticated -) - -// StatusError is the typed error returned by all SDK operations. -type StatusError = types.StatusError - -// FieldViolation identifies an invalid request field. -type FieldViolation = types.FieldViolation - -// ErrorInfo describes a server failure using a stable reason within a domain. -type ErrorInfo = types.ErrorInfo - -// IsNotFound returns true if the error indicates a resource was not found. -func IsNotFound(err error) bool { return types.IsNotFound(err) } - -// IsAlreadyExists returns true if the error indicates a resource already exists. -func IsAlreadyExists(err error) bool { return types.IsAlreadyExists(err) } - -// IsUnavailable returns true if the error indicates the service is unavailable. -func IsUnavailable(err error) bool { return types.IsUnavailable(err) } - -// IsPermissionDenied returns true if the error indicates insufficient permissions. -func IsPermissionDenied(err error) bool { return types.IsPermissionDenied(err) } - -// IsInvalidArgument returns true if the error indicates an invalid argument. -func IsInvalidArgument(err error) bool { return types.IsInvalidArgument(err) } - -// IsDeadlineExceeded returns true if the error indicates a deadline was exceeded. -func IsDeadlineExceeded(err error) bool { return types.IsDeadlineExceeded(err) } - -// IsCancelled returns true if the error indicates the operation was cancelled. -func IsCancelled(err error) bool { return types.IsCancelled(err) } - -// IsUnimplemented returns true if the error indicates the operation is not implemented. -func IsUnimplemented(err error) bool { return types.IsUnimplemented(err) } - -// IsConflict returns true if the error indicates a conflict, such as -// optimistic concurrency or an invalid state transition. -func IsConflict(err error) bool { return types.IsConflict(err) } - -// IsUnauthenticated returns true if the error indicates invalid or missing credentials. -func IsUnauthenticated(err error) bool { return types.IsUnauthenticated(err) } diff --git a/sdk/go/openshell/v1/errors_test.go b/sdk/go/openshell/v1/errors_test.go deleted file mode 100644 index e1ec0d61f5..0000000000 --- a/sdk/go/openshell/v1/errors_test.go +++ /dev/null @@ -1,127 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "errors" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestStatusError_Error(t *testing.T) { - err := &StatusError{ - Code: ErrorNotFound, - Message: "sandbox not found", - } - s := err.Error() - assert.Contains(t, s, "NotFound") - assert.Contains(t, s, "sandbox not found") -} - -func TestStatusError_ErrorWithCause(t *testing.T) { - cause := errors.New("underlying error") - err := &StatusError{ - Code: ErrorInvalidArgument, - Message: "bad name", - Cause: cause, - } - s := err.Error() - assert.Contains(t, s, "InvalidArgument") - assert.Contains(t, s, "bad name") - assert.Equal(t, cause, errors.Unwrap(err)) -} - -func TestIsNotFound(t *testing.T) { - err := &StatusError{Code: ErrorNotFound, Message: "not found"} - assert.True(t, IsNotFound(err)) - assert.False(t, IsAlreadyExists(err)) -} - -func TestIsAlreadyExists(t *testing.T) { - err := &StatusError{Code: ErrorAlreadyExists, Message: "exists"} - assert.True(t, IsAlreadyExists(err)) - assert.False(t, IsNotFound(err)) -} - -func TestIsUnavailable(t *testing.T) { - err := &StatusError{Code: ErrorUnavailable, Message: "down"} - assert.True(t, IsUnavailable(err)) -} - -func TestIsPermissionDenied(t *testing.T) { - err := &StatusError{Code: ErrorPermissionDenied, Message: "denied"} - assert.True(t, IsPermissionDenied(err)) -} - -func TestIsInvalidArgument(t *testing.T) { - err := &StatusError{Code: ErrorInvalidArgument, Message: "invalid"} - assert.True(t, IsInvalidArgument(err)) -} - -func TestIsDeadlineExceeded(t *testing.T) { - err := &StatusError{Code: ErrorDeadlineExceeded, Message: "timeout"} - assert.True(t, IsDeadlineExceeded(err)) -} - -func TestIsCancelled(t *testing.T) { - err := &StatusError{Code: ErrorCancelled, Message: "cancelled"} - assert.True(t, IsCancelled(err)) -} - -func TestIsConflict(t *testing.T) { - err := &StatusError{Code: ErrorConflict, Message: "version conflict"} - assert.True(t, IsConflict(err)) - assert.False(t, IsNotFound(err)) -} - -func TestIsHelpers_NonStatusError(t *testing.T) { - err := errors.New("plain error") - assert.False(t, IsNotFound(err)) - assert.False(t, IsAlreadyExists(err)) - assert.False(t, IsUnavailable(err)) - assert.False(t, IsPermissionDenied(err)) - assert.False(t, IsInvalidArgument(err)) - assert.False(t, IsDeadlineExceeded(err)) - assert.False(t, IsCancelled(err)) - assert.False(t, IsConflict(err)) -} - -func TestIsHelpers_NilError(t *testing.T) { - assert.False(t, IsNotFound(nil)) - assert.False(t, IsConflict(nil)) -} - -func TestStatusError_WrappedError(t *testing.T) { - inner := &StatusError{Code: ErrorNotFound, Message: "not found"} - wrapped := fmt.Errorf("operation failed: %w", inner) - assert.True(t, IsNotFound(wrapped)) - - var se *StatusError - require.True(t, errors.As(wrapped, &se)) - assert.Equal(t, ErrorNotFound, se.Code) -} - -func TestErrorCode_String(t *testing.T) { - tests := []struct { - code ErrorCode - want string - }{ - {ErrorNotFound, "NotFound"}, - {ErrorAlreadyExists, "AlreadyExists"}, - {ErrorUnavailable, "Unavailable"}, - {ErrorPermissionDenied, "PermissionDenied"}, - {ErrorInvalidArgument, "InvalidArgument"}, - {ErrorDeadlineExceeded, "DeadlineExceeded"}, - {ErrorCancelled, "Cancelled"}, - {ErrorInternal, "Internal"}, - {ErrorUnimplemented, "Unimplemented"}, - {ErrorConflict, "Conflict"}, - } - for _, tt := range tests { - assert.Equal(t, tt.want, tt.code.String()) - } -} diff --git a/sdk/go/openshell/v1/example_fake_test.go b/sdk/go/openshell/v1/example_fake_test.go deleted file mode 100644 index 162191723f..0000000000 --- a/sdk/go/openshell/v1/example_fake_test.go +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1_test - -import ( - "context" - "fmt" - "log" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/fake" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// ExampleNewClient_addSandbox demonstrates pre-seeding a fake client with -// a sandbox fixture. -func ExampleNewClient_addSandbox() { - client := fake.NewClient() - defer client.Close() //nolint:errcheck - - // Pre-seed a sandbox that already exists in Ready state - client.AddSandbox("default", &types.Sandbox{ - Name: "pre-existing", - Status: types.SandboxStatus{ - Phase: types.SandboxReady, - }, - ResourceVersion: 5, - }) - - ctx := context.Background() - - sb, err := client.Sandboxes().Get(ctx, "default", "pre-existing") - if err != nil { - log.Fatal(err) - } - fmt.Println("Name:", sb.Name) - fmt.Println("Phase:", sb.Status.Phase) - // Output: - // Name: pre-existing - // Phase: Ready -} - -// ExampleNewClient_addProvider demonstrates pre-seeding a fake client with -// a provider fixture. -func ExampleNewClient_addProvider() { - client := fake.NewClient() - defer client.Close() //nolint:errcheck - - // Pre-seed a provider - client.AddProvider("default", &types.Provider{ - Name: "seeded-provider", - Type: "openai", - }) - - ctx := context.Background() - - providers, err := client.Providers().ListAll(ctx, "default") - if err != nil { - log.Fatal(err) - } - fmt.Println("Count:", len(providers)) - fmt.Println("Name:", providers[0].Name) - // Output: - // Count: 1 - // Name: seeded-provider -} - -// ExampleNewClient_withHealthResult demonstrates configuring the fake -// health sub-client to return a custom result. -func ExampleNewClient_withHealthResult() { - client := fake.NewClient(fake.WithHealthResult(&types.HealthResult{ - Healthy: false, - Version: "1.2.3", - })) - defer client.Close() //nolint:errcheck - - ctx := context.Background() - - result, err := client.Health().Check(ctx) - if err != nil { - log.Fatal(err) - } - fmt.Println("Healthy:", result.Healthy) - fmt.Println("Version:", result.Version) - // Output: - // Healthy: false - // Version: 1.2.3 -} - -// ExampleNewClient_watchEvents demonstrates watching for sandbox events -// using the fake client. -func ExampleNewClient_watchEvents() { - client := fake.NewClient() - defer client.Close() //nolint:errcheck - - ctx := context.Background() - - // Start watching before creating - watcher, err := client.Sandboxes().Watch(ctx, "default", "my-sandbox") - if err != nil { - log.Fatal(err) - } - defer watcher.Stop() - - // Create triggers an ADDED event - _, err = client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}) - if err != nil { - log.Fatal(err) - } - - event := <-watcher.ResultChan() - fmt.Println("Type:", event.Type) - fmt.Println("Name:", event.Object.Name) - // Output: - // Type: ADDED - // Name: my-sandbox -} - -// ExampleNewClient_stopOnTerminal demonstrates the StopOnTerminal watch -// option that automatically closes the watcher when a sandbox reaches a -// terminal phase. -func ExampleNewClient_stopOnTerminal() { - client := fake.NewClient() - defer client.Close() //nolint:errcheck - - ctx := context.Background() - - // Watch with StopOnTerminal - watcher, err := client.Sandboxes().Watch(ctx, "default", "my-sandbox", v1.WatchOptions{ - StopOnTerminal: true, - }) - if err != nil { - log.Fatal(err) - } - - // Create and transition to Ready - _, err = client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}) - if err != nil { - log.Fatal(err) - } - _, err = client.Sandboxes().WaitReady(ctx, "default", "my-sandbox") - if err != nil { - log.Fatal(err) - } - - // Drain events, channel closes after terminal phase - var count int - for range watcher.ResultChan() { - count++ - } - fmt.Println("Events received:", count) - // Output: - // Events received: 2 -} diff --git a/sdk/go/openshell/v1/example_test.go b/sdk/go/openshell/v1/example_test.go deleted file mode 100644 index d7c3417f31..0000000000 --- a/sdk/go/openshell/v1/example_test.go +++ /dev/null @@ -1,184 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1_test - -import ( - "context" - "fmt" - "log" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/fake" -) - -// ExampleClient_Sandboxes demonstrates the sandbox lifecycle: create a sandbox, -// wait for it to become ready, and then clean up. -func ExampleClient_Sandboxes() { - client := fake.NewClient() - defer client.Close() //nolint:errcheck - - ctx := context.Background() - - // Create a sandbox - sb, err := client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}) - if err != nil { - log.Fatal(err) - } - fmt.Println("Phase after create:", sb.Status.Phase) - - // Wait for the sandbox to become ready - sb, err = client.Sandboxes().WaitReady(ctx, "default", "my-sandbox") - if err != nil { - log.Fatal(err) - } - fmt.Println("Phase after wait:", sb.Status.Phase) - - // Clean up - if _, err := client.Sandboxes().Delete(ctx, "default", "my-sandbox"); err != nil { - log.Fatal(err) - } - fmt.Println("Deleted") - // Output: - // Phase after create: Provisioning - // Phase after wait: Ready - // Deleted -} - -// ExampleClient_Providers demonstrates registering and listing providers. -func ExampleClient_Providers() { - client := fake.NewClient() - defer client.Close() //nolint:errcheck - - ctx := context.Background() - - // Register a provider - _, err := client.Providers().Create(ctx, "default", &v1.Provider{ - Name: "my-openai", - Type: "openai", - }) - if err != nil { - log.Fatal(err) - } - - // List all providers - providers, err := client.Providers().ListAll(ctx, "default") - if err != nil { - log.Fatal(err) - } - fmt.Println("Count:", len(providers)) - fmt.Println("Name:", providers[0].Name) - // Output: - // Count: 1 - // Name: my-openai -} - -// ExampleClient_Health demonstrates checking gateway health. -func ExampleClient_Health() { - client := fake.NewClient() - defer client.Close() //nolint:errcheck - - ctx := context.Background() - - result, err := client.Health().Check(ctx) - if err != nil { - log.Fatal(err) - } - fmt.Println("Healthy:", result.Healthy) - // Output: - // Healthy: true -} - -// ExampleClient_Exec demonstrates running a command in a sandbox. -// The fake client returns Unimplemented for exec operations, so this -// example shows the call pattern and error handling. -func ExampleClient_Exec() { - client := fake.NewClient() - defer client.Close() //nolint:errcheck - - ctx := context.Background() - - _, err := client.Exec().Run(ctx, "default", "my-sandbox", []string{"echo", "hello"}) - if v1.IsUnimplemented(err) { - fmt.Println("Exec requires a real gateway") - } - // Output: - // Exec requires a real gateway -} - -// ExampleClient_TCP demonstrates binding a local port to a sandbox port. -// The returned handle accepts and tunnels connections internally. -// -// The fake client returns Unimplemented for Listen, so this example shows -// the call pattern and error handling rather than a live tunnel. -func ExampleClient_TCP() { - client := fake.NewClient() - defer client.Close() //nolint:errcheck - - ctx := context.Background() - - // Bind local port 0 (OS-assigned) to sandbox port 8080. - ln, err := client.TCP().Listen(ctx, "default", "my-sandbox", 8080, 0) - if v1.IsUnimplemented(err) { - fmt.Println("Listen requires a real gateway") - } - if ln != nil { - // In production, dial ln.Addr() with the protocol client that should - // connect to the sandbox service. No Accept loop is required. - defer ln.Close() //nolint:errcheck - } - // Output: - // Listen requires a real gateway -} - -// ExampleIsNotFound demonstrates handling a not-found error. -func ExampleIsNotFound() { - client := fake.NewClient() - defer client.Close() //nolint:errcheck - - ctx := context.Background() - - _, err := client.Sandboxes().Get(ctx, "default", "nonexistent") - if v1.IsNotFound(err) { - fmt.Println("Sandbox not found") - } - // Output: - // Sandbox not found -} - -// ExampleIsAlreadyExists demonstrates handling a duplicate-creation error. -func ExampleIsAlreadyExists() { - client := fake.NewClient() - defer client.Close() //nolint:errcheck - - ctx := context.Background() - - // Create a sandbox - _, err := client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}) - if err != nil { - log.Fatal(err) - } - - // Try to create the same sandbox again - _, err = client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}) - if v1.IsAlreadyExists(err) { - fmt.Println("Sandbox already exists") - } - // Output: - // Sandbox already exists -} - -// ExampleIsUnavailable demonstrates detecting a closed client. -func ExampleIsUnavailable() { - client := fake.NewClient() - _ = client.Close() - - ctx := context.Background() - - _, err := client.Sandboxes().Get(ctx, "default", "any") - if v1.IsUnavailable(err) { - fmt.Println("Client is closed") - } - // Output: - // Client is closed -} diff --git a/sdk/go/openshell/v1/exec.go b/sdk/go/openshell/v1/exec.go deleted file mode 100644 index f30975d15c..0000000000 --- a/sdk/go/openshell/v1/exec.go +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// ExecResult holds the collected output of a completed command execution. -type ExecResult = types.ExecResult - -// ExecChunk represents a single chunk of output from a streaming command execution. -type ExecChunk = types.ExecChunk - -// ExecStream provides an iterator interface over streaming command output. -type ExecStream interface { - Next() (*ExecChunk, error) - ExitCode() (int, error) - Close() error -} - -// InteractiveSession provides bidirectional I/O for interactive command execution. -type InteractiveSession interface { - Read(p []byte) (int, error) - Write(p []byte) (int, error) - Resize(cols, rows uint32) error - // ExitCode waits for final stream completion. An observed exit code is - // returned alongside any later transport error. Drain Read concurrently. - ExitCode() (int, error) - Close() error -} - -// InteractiveSessionControl adds optional input closure and cancellation to -// InteractiveSession without requiring existing implementations to add methods. -// Sessions returned by this SDK implement both interfaces. -type InteractiveSessionControl interface { - InteractiveSession - // CloseWrite ends stdin and resize input without cancelling output. - CloseWrite() error - // Cancel aborts the RPC. Close retains the same full-close behavior. - Cancel() error -} - -// CloseInteractiveInput ends input while preserving output when supported. -// Unsupported sessions return ErrorUnimplemented and are left open. -func CloseInteractiveInput(session InteractiveSession) error { - if closer, ok := session.(interface{ CloseWrite() error }); ok { - return closer.CloseWrite() - } - return &StatusError{Code: ErrorUnimplemented, Message: "interactive session does not support closing input independently"} -} - -// CancelInteractive aborts a session, falling back to the original Close contract. -func CancelInteractive(session InteractiveSession) error { - if canceler, ok := session.(interface{ Cancel() error }); ok { - return canceler.Cancel() - } - return session.Close() -} - -// ExecInterface defines command execution operations on sandboxes. -// Methods accept a sandbox name and resolve it to an ID internally. -type ExecInterface interface { - Run(ctx context.Context, workspace, sandboxName string, command []string, opts ...ExecOptions) (*ExecResult, error) - Stream(ctx context.Context, workspace, sandboxName string, command []string, opts ...ExecOptions) (ExecStream, error) - Interactive(ctx context.Context, workspace, sandboxName string, command []string, cols, rows uint32, opts ...ExecOptions) (InteractiveSession, error) -} diff --git a/sdk/go/openshell/v1/exec_client.go b/sdk/go/openshell/v1/exec_client.go deleted file mode 100644 index 56b13d928f..0000000000 --- a/sdk/go/openshell/v1/exec_client.go +++ /dev/null @@ -1,341 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "io" - "sync" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" - "google.golang.org/grpc/status" -) - -type execClient struct { - client pb.OpenShellClient - sandboxes SandboxInterface -} - -func newExecClient(conn grpc.ClientConnInterface, sandboxes SandboxInterface) *execClient { - return &execClient{client: pb.NewOpenShellClient(conn), sandboxes: sandboxes} -} - -func (e *execClient) Run(ctx context.Context, workspace, sandboxName string, command []string, opts ...ExecOptions) (*ExecResult, error) { - if sandboxName == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - owner, err := sandboxOwner(ctx, e.sandboxes, workspace, sandboxName) - if err != nil { - return nil, err - } - var opt *ExecOptions - if len(opts) > 0 { - opt = &opts[0] - } - req := converter.ExecRequestToProto(sandboxName, command, opt) - req.WorkspaceScope = namedWorkspaceScope(workspace) - - stream, err := openRouted(ctx, owner, func(ctx context.Context) (grpc.ServerStreamingClient[pb.ExecSandboxEvent], error) { - return e.client.ExecSandbox(ctx, req) - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - - var events []*pb.ExecSandboxEvent - for { - ev, recvErr := stream.Recv() - if recvErr == io.EOF { - break - } - if recvErr != nil { - return nil, converter.FromGRPCError(recvErr) - } - events = append(events, ev) - } - - return converter.ExecResultFromEvents(events) -} - -func (e *execClient) Stream(ctx context.Context, workspace, sandboxName string, command []string, opts ...ExecOptions) (ExecStream, error) { - if sandboxName == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - owner, err := sandboxOwner(ctx, e.sandboxes, workspace, sandboxName) - if err != nil { - return nil, err - } - var opt *ExecOptions - if len(opts) > 0 { - opt = &opts[0] - } - req := converter.ExecRequestToProto(sandboxName, command, opt) - req.WorkspaceScope = namedWorkspaceScope(workspace) - - streamCtx, cancel := context.WithCancel(ctx) - stream, err := openRouted(streamCtx, owner, func(ctx context.Context) (grpc.ServerStreamingClient[pb.ExecSandboxEvent], error) { - return e.client.ExecSandbox(ctx, req) - }) - if err != nil { - cancel() - return nil, converter.FromGRPCError(err) - } - - return &execStream{stream: stream, cancel: cancel}, nil -} - -func (e *execClient) Interactive(ctx context.Context, workspace, sandboxName string, command []string, cols, rows uint32, opts ...ExecOptions) (InteractiveSession, error) { - if sandboxName == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - owner, err := sandboxOwner(ctx, e.sandboxes, workspace, sandboxName) - if err != nil { - return nil, err - } - var opt *ExecOptions - if len(opts) > 0 { - opt = &opts[0] - } - - startReq := converter.ExecInteractiveRequestToProto(sandboxName, command, cols, rows, opt) - startReq.WorkspaceScope = namedWorkspaceScope(workspace) - - streamCtx, cancel := context.WithCancel(ctx) - start := &pb.ExecSandboxInput{Payload: &pb.ExecSandboxInput_Start{Start: startReq}} - stream, err := openBidiRouted(streamCtx, owner, start, e.client.ExecSandboxInteractive) - if err != nil { - cancel() - return nil, converter.FromGRPCError(err) - } - - return newInteractiveSession(streamCtx, cancel, stream), nil -} - -// execStream wraps a server-streaming RPC into the ExecStream interface. -type execStream struct { - stream grpc.ServerStreamingClient[pb.ExecSandboxEvent] - cancel context.CancelFunc - exitCode int - exited bool - hasExit bool -} - -func (s *execStream) Next() (*ExecChunk, error) { - if s.exited { - return nil, io.EOF - } - - ev, err := s.stream.Recv() - if err == io.EOF { - return nil, io.EOF - } - if err != nil { - return nil, converter.FromGRPCError(err) - } - - chunk, code, convErr := converter.ExecChunkFromEvent(ev) - if convErr != nil { - return nil, convErr - } - if chunk != nil { - return chunk, nil - } - // nil chunk with no error means exit event - s.exitCode = code - s.exited = true - s.hasExit = true - return nil, io.EOF -} - -func (s *execStream) ExitCode() (int, error) { - if !s.exited { - for { - _, err := s.Next() - if err == io.EOF { - break - } - if err != nil { - return -1, err - } - } - } - if !s.hasExit { - return -1, &StatusError{Code: ErrorInternal, Message: "stream ended without exit event"} - } - return s.exitCode, nil -} - -func (s *execStream) Close() error { - if s.cancel != nil { - s.cancel() - } - return nil -} - -// interactiveSession wraps a bidirectional streaming RPC into the InteractiveSession interface. -// A background goroutine owns the Recv loop and routes events to dataCh (for Read) -// and publishes the final exit/status through done, preventing concurrent Recv calls. -type interactiveSession struct { - stream grpc.BidiStreamingClient[pb.ExecSandboxInput, pb.ExecSandboxEvent] - cancel context.CancelFunc - sendMu sync.Mutex - inputClosed bool - inputCloseErr error - closeOnce sync.Once - closeErr error - dataCh chan []byte - done chan struct{} - errOnce sync.Once - err error - buf []byte - - exitCode int - hasExitCode bool -} - -func newInteractiveSession(ctx context.Context, cancel context.CancelFunc, stream grpc.BidiStreamingClient[pb.ExecSandboxInput, pb.ExecSandboxEvent]) *interactiveSession { - s := &interactiveSession{ - stream: stream, - cancel: cancel, - dataCh: make(chan []byte, 64), - done: make(chan struct{}), - } - go s.readLoop(ctx) - return s -} - -func (s *interactiveSession) setErr(err error) { - s.errOnce.Do(func() { s.err = err }) -} - -func (s *interactiveSession) readLoop(ctx context.Context) { - defer s.cancel() - defer close(s.dataCh) - defer close(s.done) - for { - ev, err := s.stream.Recv() - if err != nil { - if err != io.EOF { - s.setErr(converter.FromGRPCError(err)) - } else if ctx.Err() != nil { - s.setErr(converter.FromGRPCError(status.FromContextError(ctx.Err()).Err())) - } else if !s.hasExitCode { - s.setErr(&StatusError{Code: ErrorInternal, Message: "stream ended without exit event"}) - } - return - } - - if s.hasExitCode { - s.setErr(&StatusError{Code: ErrorInternal, Message: "received event after exit"}) - return - } - chunk, code, convErr := converter.ExecChunkFromEvent(ev) - if convErr != nil { - s.setErr(convErr) - return - } - // nil chunk with no error means exit event - if chunk == nil { - s.exitCode = code - s.hasExitCode = true - continue - } - select { - case s.dataCh <- chunk.Data: - case <-ctx.Done(): - s.setErr(converter.FromGRPCError(status.FromContextError(ctx.Err()).Err())) - return - } - } -} - -func (s *interactiveSession) Read(p []byte) (int, error) { - if len(s.buf) > 0 { - n := copy(p, s.buf) - s.buf = s.buf[n:] - return n, nil - } - - data, ok := <-s.dataCh - if !ok { - if s.err != nil { - return 0, s.err - } - return 0, io.EOF - } - n := copy(p, data) - if n < len(data) { - s.buf = append(s.buf, data[n:]...) - } - return n, nil -} - -func (s *interactiveSession) Write(p []byte) (int, error) { - s.sendMu.Lock() - defer s.sendMu.Unlock() - if s.inputClosed { - return 0, io.ErrClosedPipe - } - err := s.stream.Send(&pb.ExecSandboxInput{ - Payload: &pb.ExecSandboxInput_Stdin{Stdin: p}, - }) - if err != nil { - return 0, converter.FromGRPCError(err) - } - return len(p), nil -} - -func (s *interactiveSession) Resize(cols, rows uint32) error { - s.sendMu.Lock() - defer s.sendMu.Unlock() - if s.inputClosed { - return io.ErrClosedPipe - } - err := s.stream.Send(&pb.ExecSandboxInput{ - Payload: &pb.ExecSandboxInput_Resize{ - Resize: &pb.ExecSandboxWindowResize{ - Cols: cols, - Rows: rows, - }, - }, - }) - if err != nil { - return converter.FromGRPCError(err) - } - return nil -} - -func (s *interactiveSession) ExitCode() (int, error) { - <-s.done - if s.hasExitCode { - return s.exitCode, s.err - } - return -1, s.err -} - -func (s *interactiveSession) CloseWrite() error { - s.sendMu.Lock() - defer s.sendMu.Unlock() - if !s.inputClosed { - s.inputClosed = true - s.inputCloseErr = s.stream.CloseSend() - } - return s.inputCloseErr -} - -func (s *interactiveSession) Cancel() error { - s.closeOnce.Do(func() { - // Cancel before taking sendMu so a blocked Write can release it. - s.cancel() - s.closeErr = s.CloseWrite() - <-s.done - }) - return s.closeErr -} - -func (s *interactiveSession) Close() error { - return s.Cancel() -} diff --git a/sdk/go/openshell/v1/exec_client_test.go b/sdk/go/openshell/v1/exec_client_test.go deleted file mode 100644 index fbf5140311..0000000000 --- a/sdk/go/openshell/v1/exec_client_test.go +++ /dev/null @@ -1,764 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "io" - "net" - "sync" - "testing" - "time" - - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" -) - -// stubSandboxResolver implements SandboxInterface for testing name-to-ID resolution. -// Get returns a Sandbox with ID = "sb-" + name. All other methods panic. -type stubSandboxResolver struct { - getErr error // if non-nil, Get returns this error -} - -func (r *stubSandboxResolver) Get(_ context.Context, _, name string) (*Sandbox, error) { - if r.getErr != nil { - return nil, r.getErr - } - return &Sandbox{ID: "sb-" + name, Name: name}, nil -} - -func (r *stubSandboxResolver) Create(context.Context, string, string, *SandboxSpec, ...CreateOption) (*Sandbox, error) { - panic("not implemented") -} -func (r *stubSandboxResolver) List(string, ...ListOptions) (*Pager[*Sandbox], error) { - panic("not implemented") -} -func (r *stubSandboxResolver) ListAll(context.Context, string, ...ListOptions) ([]*Sandbox, error) { - panic("not implemented") -} -func (r *stubSandboxResolver) Delete(context.Context, string, string, ...DeleteOptions) (*DeletionResult, error) { - panic("not implemented") -} -func (r *stubSandboxResolver) AttachProvider(context.Context, string, string, string, uint64) (*AttachProviderResult, error) { - panic("not implemented") -} -func (r *stubSandboxResolver) DetachProvider(context.Context, string, string, string, uint64) (*DetachProviderResult, error) { - panic("not implemented") -} -func (r *stubSandboxResolver) ListProviders(string, string, ...ListOptions) (*Pager[*Provider], error) { - panic("not implemented") -} -func (r *stubSandboxResolver) ListAllProviders(context.Context, string, string, ...ListOptions) ([]*Provider, error) { - panic("not implemented") -} -func (r *stubSandboxResolver) WaitReady(context.Context, string, string, ...WaitOptions) (*Sandbox, error) { - panic("not implemented") -} -func (r *stubSandboxResolver) Watch(context.Context, string, string, ...WatchOptions) (WatchInterface[*Sandbox], error) { - panic("not implemented") -} -func (r *stubSandboxResolver) GetLogs(context.Context, string, string, ...LogOption) (*LogResult, error) { - panic("not implemented") -} -func (r *stubSandboxResolver) Stop(context.Context, string, string) (*Sandbox, error) { - panic("not implemented") -} -func (r *stubSandboxResolver) Start(context.Context, string, string) (*Sandbox, error) { - panic("not implemented") -} -func (r *stubSandboxResolver) WaitStopped(context.Context, string, string, ...WaitOptions) (*Sandbox, error) { - panic("not implemented") -} - -type mockExecServer struct { - pb.UnimplementedOpenShellServer - mu sync.Mutex - execEvents []*pb.ExecSandboxEvent - execErr error - lastExecRequest *pb.ExecSandboxRequest - - interactiveEvents []*pb.ExecSandboxEvent - interactiveErr error - interactiveWaitInput bool - interactiveBlock bool - interactiveWaitEOF bool - interactiveFinalErr error - receivedInputs []*pb.ExecSandboxInput -} - -func newMockExecServer() *mockExecServer { - return &mockExecServer{} -} - -func (s *mockExecServer) ExecSandbox(req *pb.ExecSandboxRequest, stream grpc.ServerStreamingServer[pb.ExecSandboxEvent]) error { - s.mu.Lock() - s.lastExecRequest = req - events := make([]*pb.ExecSandboxEvent, len(s.execEvents)) - copy(events, s.execEvents) - execErr := s.execErr - s.mu.Unlock() - - if execErr != nil { - return execErr - } - - for _, ev := range events { - if err := stream.Send(ev); err != nil { - return err - } - } - return nil -} - -func (s *mockExecServer) ExecSandboxInteractive(stream grpc.BidiStreamingServer[pb.ExecSandboxInput, pb.ExecSandboxEvent]) error { - s.mu.Lock() - interactiveErr := s.interactiveErr - interactiveBlock := s.interactiveBlock - waitEOF := s.interactiveWaitEOF - finalErr := s.interactiveFinalErr - events := make([]*pb.ExecSandboxEvent, len(s.interactiveEvents)) - copy(events, s.interactiveEvents) - s.mu.Unlock() - - if interactiveErr != nil { - return interactiveErr - } - - // Read the start message - startMsg, err := stream.Recv() - if err != nil { - return err - } - s.mu.Lock() - s.receivedInputs = append(s.receivedInputs, startMsg) - s.mu.Unlock() - if interactiveBlock { - <-stream.Context().Done() - return stream.Context().Err() - } - if waitEOF { - for { - _, recvErr := stream.Recv() - if recvErr == io.EOF { - break - } - if recvErr != nil { - return recvErr - } - } - } - - s.mu.Lock() - waitInput := s.interactiveWaitInput - s.mu.Unlock() - - if waitInput { - msg, recvErr := stream.Recv() - if recvErr != nil { - return recvErr - } - s.mu.Lock() - s.receivedInputs = append(s.receivedInputs, msg) - s.mu.Unlock() - } - - // Read subsequent messages until client closes, collecting them - go func() { - for { - msg, recvErr := stream.Recv() - if recvErr != nil { - return - } - s.mu.Lock() - s.receivedInputs = append(s.receivedInputs, msg) - s.mu.Unlock() - } - }() - - // Send canned events - for _, ev := range events { - if err := stream.Send(ev); err != nil { - return err - } - } - return finalErr -} - -func setupExecTest(t *testing.T, mock *mockExecServer) (*execClient, func()) { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - - return newExecClient(conn, &stubSandboxResolver{}), func() { - _ = conn.Close() - srv.Stop() - } -} - -// --- T043: Run and Stream tests --- - -func TestExecRun(t *testing.T) { - mock := newMockExecServer() - mock.execEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("hello ")}}}, - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("world\n")}}}, - {Payload: &pb.ExecSandboxEvent_Stderr{Stderr: &pb.ExecSandboxStderr{Data: []byte("warn\n")}}}, - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 0}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - result, err := client.Run(context.Background(), "default", "test-sandbox", []string{"echo", "hello", "world"}) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, 0, result.ExitCode) - assert.Equal(t, []byte("hello world\n"), result.Stdout) - assert.Equal(t, []byte("warn\n"), result.Stderr) -} - -func TestExecRun_WithOptions(t *testing.T) { - mock := newMockExecServer() - mock.execEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 0}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - opts := ExecOptions{ - Env: map[string]string{"FOO": "bar"}, - WorkDir: "/tmp", - } - result, err := client.Run(context.Background(), "default", "test-sandbox", []string{"ls"}, opts) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, 0, result.ExitCode) - - mock.mu.Lock() - defer mock.mu.Unlock() - assert.Equal(t, "test-sandbox", mock.lastExecRequest.GetSandbox()) - assert.Equal(t, []string{"ls"}, mock.lastExecRequest.GetCommand()) - assert.Equal(t, "/tmp", mock.lastExecRequest.GetWorkdir()) - assert.Equal(t, map[string]string{"FOO": "bar"}, mock.lastExecRequest.GetEnvironment()) -} - -func TestExecRun_NonZeroExit(t *testing.T) { - mock := newMockExecServer() - mock.execEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Stderr{Stderr: &pb.ExecSandboxStderr{Data: []byte("fail\n")}}}, - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 1}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - result, err := client.Run(context.Background(), "default", "test-sandbox", []string{"false"}) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, 1, result.ExitCode) - assert.Empty(t, result.Stdout) - assert.Equal(t, []byte("fail\n"), result.Stderr) -} - -func TestExecRun_ServerError(t *testing.T) { - mock := newMockExecServer() - mock.execErr = status.Error(codes.NotFound, "sandbox not found") - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - _, err := client.Run(context.Background(), "default", "missing-sandbox", []string{"ls"}) - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestExecStream(t *testing.T) { - mock := newMockExecServer() - mock.execEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("line1\n")}}}, - {Payload: &pb.ExecSandboxEvent_Stderr{Stderr: &pb.ExecSandboxStderr{Data: []byte("err1\n")}}}, - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("line2\n")}}}, - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 42}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - stream, err := client.Stream(context.Background(), "default", "test-sandbox", []string{"cat"}) - require.NoError(t, err) - require.NotNil(t, stream) - defer func() { _ = stream.Close() }() - - chunk1, err := stream.Next() - require.NoError(t, err) - assert.Equal(t, StreamStdout, chunk1.Stream) - assert.Equal(t, []byte("line1\n"), chunk1.Data) - - chunk2, err := stream.Next() - require.NoError(t, err) - assert.Equal(t, StreamStderr, chunk2.Stream) - assert.Equal(t, []byte("err1\n"), chunk2.Data) - - chunk3, err := stream.Next() - require.NoError(t, err) - assert.Equal(t, StreamStdout, chunk3.Stream) - assert.Equal(t, []byte("line2\n"), chunk3.Data) - - // Next call after exit should return io.EOF - _, err = stream.Next() - assert.ErrorIs(t, err, io.EOF) - - exitCode, err := stream.ExitCode() - require.NoError(t, err) - assert.Equal(t, 42, exitCode) -} - -func TestExecStream_ServerError(t *testing.T) { - mock := newMockExecServer() - mock.execErr = status.Error(codes.Internal, "internal error") - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - stream, err := client.Stream(context.Background(), "default", "test-sandbox", []string{"ls"}) - if err != nil { - var se *StatusError - require.ErrorAs(t, err, &se) - assert.Equal(t, ErrorInternal, se.Code) - return - } - _, err = stream.Next() - require.Error(t, err) -} - -func TestExecStream_EmptyOutput(t *testing.T) { - mock := newMockExecServer() - mock.execEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 0}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - stream, err := client.Stream(context.Background(), "default", "test-sandbox", []string{"true"}) - require.NoError(t, err) - defer func() { _ = stream.Close() }() - - _, err = stream.Next() - assert.ErrorIs(t, err, io.EOF) - - exitCode, err := stream.ExitCode() - require.NoError(t, err) - assert.Equal(t, 0, exitCode) -} - -// --- T044: Interactive session tests --- - -func TestExecInteractive(t *testing.T) { - mock := newMockExecServer() - mock.interactiveEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("$ ")}}}, - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("output\n")}}}, - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 0}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - session, err := client.Interactive(context.Background(), "default", "test-sandbox", []string{"/bin/bash"}, 80, 24) - require.NoError(t, err) - require.NotNil(t, session) - defer func() { _ = session.Close() }() - - // Read output - buf := make([]byte, 1024) - n, err := session.Read(buf) - require.NoError(t, err) - assert.Equal(t, "$ ", string(buf[:n])) - - // Verify start message was received - mock.mu.Lock() - require.GreaterOrEqual(t, len(mock.receivedInputs), 1) - startInput := mock.receivedInputs[0] - mock.mu.Unlock() - - startReq := startInput.GetStart() - require.NotNil(t, startReq) - assert.Equal(t, "test-sandbox", startReq.GetSandbox()) - assert.Equal(t, []string{"/bin/bash"}, startReq.GetCommand()) - assert.True(t, startReq.GetTty()) - assert.Equal(t, uint32(80), startReq.GetCols()) - assert.Equal(t, uint32(24), startReq.GetRows()) -} - -func TestExecInteractive_CloseCancelsReceiveStream(t *testing.T) { - mock := newMockExecServer() - mock.interactiveBlock = true - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - session, err := client.Interactive(context.Background(), "default", "test-sandbox", []string{"/bin/sh"}, 80, 24) - require.NoError(t, err) - - closed := make(chan error, 1) - go func() { closed <- session.Close() }() - - select { - case err := <-closed: - require.NoError(t, err) - case <-time.After(time.Second): - t.Fatal("InteractiveSession.Close did not cancel the receive stream") - } -} - -func TestExecInteractive_CloseWriteDrainsOutput(t *testing.T) { - mock := newMockExecServer() - mock.interactiveWaitEOF = true - mock.interactiveEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("after EOF")}}}, - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 7}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - session, err := client.Interactive(ctx, "default", "test-sandbox", []string{"cat"}, 0, 0) - require.NoError(t, err) - defer func() { _ = session.Close() }() - require.Implements(t, (*InteractiveSessionControl)(nil), session) - require.NoError(t, CloseInteractiveInput(session)) - require.NoError(t, CloseInteractiveInput(session)) - _, err = session.Write([]byte("late")) - require.ErrorIs(t, err, io.ErrClosedPipe) - require.ErrorIs(t, session.Resize(80, 24), io.ErrClosedPipe) - output, err := io.ReadAll(session) - require.NoError(t, err) - require.Equal(t, "after EOF", string(output)) - code, err := session.ExitCode() - require.NoError(t, err) - require.Equal(t, 7, code) -} - -func TestExecInteractive_RetainsExitOnTerminalFailure(t *testing.T) { - for _, finalErr := range []error{status.Error(codes.Unavailable, "connection lost"), status.Error(codes.Canceled, "cancelled")} { - t.Run(status.Code(finalErr).String(), func(t *testing.T) { - mock := newMockExecServer() - mock.interactiveEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 7}}}, - } - mock.interactiveFinalErr = finalErr - client, cleanup := setupExecTest(t, mock) - defer cleanup() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - session, err := client.Interactive(ctx, "default", "test-sandbox", []string{"true"}, 0, 0) - require.NoError(t, err) - defer func() { _ = session.Close() }() - code, err := session.ExitCode() - require.Error(t, err) - require.Equal(t, 7, code) - var sdkErr *StatusError - require.ErrorAs(t, err, &sdkErr) - require.Equal(t, status.Code(finalErr), status.Code(sdkErr.Cause)) - codeAgain, errAgain := session.ExitCode() - require.Equal(t, code, codeAgain) - require.Equal(t, err, errAgain) - }) - } -} - -func TestExecInteractive_RejectsEventsAfterExit(t *testing.T) { - for name, event := range map[string]*pb.ExecSandboxEvent{ - "duplicate exit": {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 1}}}, - "late output": {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("late")}}}, - } { - t.Run(name, func(t *testing.T) { - mock := newMockExecServer() - mock.interactiveEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 0}}}, event, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - session, err := client.Interactive(ctx, "default", "test-sandbox", []string{"true"}, 0, 0) - require.NoError(t, err) - defer func() { _ = session.Close() }() - code, err := session.ExitCode() - require.Equal(t, 0, code) - require.ErrorContains(t, err, "after exit") - }) - } -} - -func TestExecInteractive_Write(t *testing.T) { - mock := newMockExecServer() - mock.interactiveWaitInput = true - mock.interactiveEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("$ ")}}}, - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 0}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - session, err := client.Interactive(context.Background(), "default", "test-sandbox", []string{"/bin/sh"}, 80, 24) - require.NoError(t, err) - defer func() { _ = session.Close() }() - - n, err := session.Write([]byte("ls\n")) - require.NoError(t, err) - assert.Equal(t, 3, n) -} - -func TestExecInteractive_Resize(t *testing.T) { - mock := newMockExecServer() - mock.interactiveWaitInput = true - mock.interactiveEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("$ ")}}}, - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 0}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - session, err := client.Interactive(context.Background(), "default", "test-sandbox", []string{"/bin/sh"}, 80, 24) - require.NoError(t, err) - defer func() { _ = session.Close() }() - - err = session.Resize(120, 40) - require.NoError(t, err) -} - -func TestExecInteractive_ServerError(t *testing.T) { - mock := newMockExecServer() - mock.interactiveErr = status.Error(codes.PermissionDenied, "not allowed") - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - session, err := client.Interactive(context.Background(), "default", "test-sandbox", []string{"/bin/sh"}, 80, 24) - if err != nil { - assert.True(t, IsPermissionDenied(err), "expected permission denied, got: %v", err) - return - } - buf := make([]byte, 1024) - _, err = session.Read(buf) - require.Error(t, err) -} - -func TestExecInteractive_ExitCode(t *testing.T) { - mock := newMockExecServer() - mock.interactiveEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("done\n")}}}, - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 130}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - session, err := client.Interactive(context.Background(), "default", "test-sandbox", []string{"/bin/sh"}, 80, 24) - require.NoError(t, err) - - // Drain output - buf := make([]byte, 1024) - for { - _, readErr := session.Read(buf) - if readErr != nil { - break - } - } - - exitCode, err := session.ExitCode() - require.NoError(t, err) - assert.Equal(t, 130, exitCode) - - _ = session.Close() -} - -func TestExecInteractive_ConcurrentReadAndExitCode(t *testing.T) { - mock := newMockExecServer() - mock.interactiveEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("line1\n")}}}, - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("line2\n")}}}, - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("line3\n")}}}, - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 0}}}, - } - - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - session, err := client.Interactive(context.Background(), "default", "test-sandbox", []string{"sh"}, 80, 24) - require.NoError(t, err) - - var wg sync.WaitGroup - var readData []byte - var readErr error - - wg.Add(1) - go func() { - defer wg.Done() - buf := make([]byte, 1024) - for { - n, err := session.Read(buf) - if err != nil { - readErr = err - return - } - readData = append(readData, buf[:n]...) - } - }() - - exitCode, exitErr := session.ExitCode() - wg.Wait() - - require.NoError(t, exitErr) - assert.Equal(t, 0, exitCode) - assert.Equal(t, io.EOF, readErr) - assert.Contains(t, string(readData), "line1\n") -} - -// --- Name-to-ID resolution tests --- - -func TestExecRun_UsesName(t *testing.T) { - mock := newMockExecServer() - mock.execEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 0}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - _, err := client.Run(context.Background(), "default", "my-sandbox", []string{"echo", "hi"}) - require.NoError(t, err) - - mock.mu.Lock() - defer mock.mu.Unlock() - // Verify the proto request contains the resolved ID, not the name - assert.Equal(t, "my-sandbox", mock.lastExecRequest.GetSandbox()) -} - -func TestExecRun_ResolutionError(t *testing.T) { - resolver := &stubSandboxResolver{ - getErr: &StatusError{Code: ErrorNotFound, Message: "sandbox not found"}, - } - client := newExecClient(stubConn(t), resolver) - - _, err := client.Run(context.Background(), "default", "nonexistent", []string{"ls"}) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestExecStream_UsesName(t *testing.T) { - mock := newMockExecServer() - mock.execEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 0}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - stream, err := client.Stream(context.Background(), "default", "my-sandbox", []string{"echo"}) - require.NoError(t, err) - - _, _ = stream.ExitCode() - _ = stream.Close() - - mock.mu.Lock() - defer mock.mu.Unlock() - assert.Equal(t, "my-sandbox", mock.lastExecRequest.GetSandbox()) -} - -func TestExecStream_ResolutionError(t *testing.T) { - resolver := &stubSandboxResolver{ - getErr: &StatusError{Code: ErrorNotFound, Message: "sandbox not found"}, - } - client := newExecClient(stubConn(t), resolver) - - _, err := client.Stream(context.Background(), "default", "nonexistent", []string{"ls"}) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestExecInteractive_UsesName(t *testing.T) { - mock := newMockExecServer() - mock.interactiveEvents = []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 0}}}, - } - client, cleanup := setupExecTest(t, mock) - defer cleanup() - - session, err := client.Interactive(context.Background(), "default", "my-sandbox", []string{"/bin/sh"}, 80, 24) - require.NoError(t, err) - - _, _ = session.ExitCode() - _ = session.Close() - - mock.mu.Lock() - defer mock.mu.Unlock() - require.NotEmpty(t, mock.receivedInputs) - startReq := mock.receivedInputs[0].GetStart() - require.NotNil(t, startReq) - assert.Equal(t, "my-sandbox", startReq.GetSandbox()) -} - -func TestExecInteractive_ResolutionError(t *testing.T) { - resolver := &stubSandboxResolver{ - getErr: &StatusError{Code: ErrorNotFound, Message: "sandbox not found"}, - } - client := newExecClient(stubConn(t), resolver) - - _, err := client.Interactive(context.Background(), "default", "nonexistent", []string{"/bin/sh"}, 80, 24) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestExecRun_EmptySandboxName(t *testing.T) { - client := newExecClient(stubConn(t), &stubSandboxResolver{}) - _, err := client.Run(context.Background(), "default", "", []string{"ls"}) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestExecStream_EmptySandboxName(t *testing.T) { - client := newExecClient(stubConn(t), &stubSandboxResolver{}) - _, err := client.Stream(context.Background(), "default", "", []string{"ls"}) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestExecInteractive_EmptySandboxName(t *testing.T) { - client := newExecClient(stubConn(t), &stubSandboxResolver{}) - _, err := client.Interactive(context.Background(), "default", "", []string{"/bin/sh"}, 80, 24) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -// stubConn creates a minimal gRPC connection for resolution-error tests -// where the RPC is never reached. -func stubConn(t *testing.T) *grpc.ClientConn { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, newMockExecServer()) - go func() { _ = srv.Serve(lis) }() - t.Cleanup(func() { srv.Stop() }) - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - t.Cleanup(func() { _ = conn.Close() }) - return conn -} diff --git a/sdk/go/openshell/v1/exec_control_test.go b/sdk/go/openshell/v1/exec_control_test.go deleted file mode 100644 index 3e2392a821..0000000000 --- a/sdk/go/openshell/v1/exec_control_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "errors" - "io" - "testing" - - "github.com/stretchr/testify/require" -) - -// Deliberately implements only the original interface: downstream mocks must -// continue compiling without adding the optional lifecycle methods. -type legacyInteractiveSession struct { - closed bool - err error -} - -var _ InteractiveSession = (*legacyInteractiveSession)(nil) - -func (*legacyInteractiveSession) Read([]byte) (int, error) { return 0, io.EOF } -func (*legacyInteractiveSession) Write(p []byte) (int, error) { return len(p), nil } -func (*legacyInteractiveSession) Resize(uint32, uint32) error { return nil } -func (*legacyInteractiveSession) ExitCode() (int, error) { return 0, nil } -func (s *legacyInteractiveSession) Close() error { s.closed = true; return s.err } - -type controlledInteractiveSession struct { - legacyInteractiveSession - inputClosed bool - cancelled bool -} - -func (s *controlledInteractiveSession) CloseWrite() error { s.inputClosed = true; return s.err } -func (s *controlledInteractiveSession) Cancel() error { s.cancelled = true; return s.err } - -func TestInteractiveControlLegacyCompatibility(t *testing.T) { - s := &legacyInteractiveSession{err: errors.New("close error")} - var statusErr *StatusError - require.ErrorAs(t, CloseInteractiveInput(s), &statusErr) - require.Equal(t, ErrorUnimplemented, statusErr.Code) - require.False(t, s.closed, "unsupported input closure must not cancel the session") - require.ErrorIs(t, CancelInteractive(s), s.err) - require.True(t, s.closed) -} - -func TestInteractiveControlDispatch(t *testing.T) { - s := &controlledInteractiveSession{legacyInteractiveSession: legacyInteractiveSession{err: errors.New("control error")}} - require.Implements(t, (*InteractiveSessionControl)(nil), s) - require.ErrorIs(t, CloseInteractiveInput(s), s.err) - require.True(t, s.inputClosed) - require.ErrorIs(t, CancelInteractive(s), s.err) - require.True(t, s.cancelled) - require.False(t, s.closed, "explicit cancellation takes precedence over fallback") -} diff --git a/sdk/go/openshell/v1/fake/broadcaster.go b/sdk/go/openshell/v1/fake/broadcaster.go deleted file mode 100644 index 2f0e38dbc5..0000000000 --- a/sdk/go/openshell/v1/fake/broadcaster.go +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "sync" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -const watchChannelBuffer = 100 - -// watchBroadcaster manages a set of watchers and broadcasts events to them. -// Each watcher can optionally filter events by resource name. -type watchBroadcaster[T any] struct { - mu sync.Mutex - watchers []*fakeWatcher[T] -} - -// newWatchBroadcaster creates a new watchBroadcaster. -func newWatchBroadcaster[T any]() *watchBroadcaster[T] { - return &watchBroadcaster[T]{} -} - -// Watch registers a new watcher. If name is non-empty, the watcher only -// receives events matching that name. If name is empty, all events are -// delivered. The returned WatchInterface must be stopped by the caller. -func (b *watchBroadcaster[T]) Watch(name string) types.WatchInterface[T] { - w := &fakeWatcher[T]{ - ch: make(chan types.Event[T], watchChannelBuffer), - name: name, - wake: make(chan struct{}, 1), - stopCh: make(chan struct{}), - done: make(chan struct{}), - } - go w.run() - - b.mu.Lock() - b.watchers = append(b.watchers, w) - b.mu.Unlock() - - return w -} - -// Broadcast sends an event to all registered watchers whose name filter -// matches (or whose filter is empty). Stopped watchers are skipped and -// cleaned up lazily. -func (b *watchBroadcaster[T]) Broadcast(event types.Event[T], name string) { - b.mu.Lock() - defer b.mu.Unlock() - - active := b.watchers[:0] - for _, w := range b.watchers { - if w.isStopped() { - continue - } - active = append(active, w) - - if w.name != "" && w.name != name { - continue - } - - w.send(event) - } - b.watchers = active -} - -// StopAll closes all active watchers. -func (b *watchBroadcaster[T]) StopAll() { - b.mu.Lock() - defer b.mu.Unlock() - - for _, w := range b.watchers { - w.Stop() - } - b.watchers = nil -} - -// fakeWatcher implements types.WatchInterface[T] with a buffered channel -// and optional name filter. -type fakeWatcher[T any] struct { - ch chan types.Event[T] - name string - once sync.Once - stopped bool - mu sync.Mutex - queue []types.Event[T] - wake chan struct{} - stopCh chan struct{} - done chan struct{} -} - -// ResultChan returns the channel delivering watch events. -func (w *fakeWatcher[T]) ResultChan() <-chan types.Event[T] { - return w.ch -} - -// send delivers an event to the watcher under its lock, preventing a -// race between Broadcast (send) and Stop (close) on w.ch. -func (w *fakeWatcher[T]) send(event types.Event[T]) { - w.mu.Lock() - defer w.mu.Unlock() - if w.stopped { - return - } - w.queue = append(w.queue, event) - select { - case w.wake <- struct{}{}: - default: - } -} - -// Stop closes the event channel. It is safe to call multiple times. -func (w *fakeWatcher[T]) Stop() { - w.once.Do(func() { - w.mu.Lock() - w.stopped = true - w.mu.Unlock() - close(w.stopCh) - <-w.done - }) -} - -// isStopped returns true if Stop has been called. -func (w *fakeWatcher[T]) isStopped() bool { - w.mu.Lock() - defer w.mu.Unlock() - return w.stopped -} - -func (w *fakeWatcher[T]) run() { - defer close(w.done) - defer close(w.ch) - for { - w.mu.Lock() - if len(w.queue) > 0 { - event := w.queue[0] - w.queue = w.queue[1:] - w.mu.Unlock() - select { - case w.ch <- event: - case <-w.stopCh: - return - } - continue - } - w.mu.Unlock() - select { - case <-w.wake: - case <-w.stopCh: - return - } - } -} diff --git a/sdk/go/openshell/v1/fake/broadcaster_test.go b/sdk/go/openshell/v1/fake/broadcaster_test.go deleted file mode 100644 index 8f170df885..0000000000 --- a/sdk/go/openshell/v1/fake/broadcaster_test.go +++ /dev/null @@ -1,195 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- T005: watchBroadcaster tests --- - -func TestWatchBroadcaster_Watch_ReceivesEvents(t *testing.T) { - b := newWatchBroadcaster[*testItem]() - - w := b.Watch("") - defer w.Stop() - - item := &testItem{Name: "alpha", Value: "v1"} - b.Broadcast(types.Event[*testItem]{Type: types.EventAdded, Object: item}, "alpha") - - select { - case ev := <-w.ResultChan(): - assert.Equal(t, types.EventAdded, ev.Type) - assert.Equal(t, "alpha", ev.Object.Name) - case <-time.After(time.Second): - t.Fatal("timed out waiting for event") - } -} - -func TestWatchBroadcaster_DoesNotDropBurstEvents(t *testing.T) { - b := newWatchBroadcaster[*testItem]() - w := b.Watch("") - defer w.Stop() - - const count = watchChannelBuffer + 50 - for i := range count { - b.Broadcast(types.Event[*testItem]{Object: &testItem{Name: fmt.Sprint(i)}}, "") - } - for range count { - select { - case <-w.ResultChan(): - case <-time.After(time.Second): - t.Fatal("watch event was dropped") - } - } -} - -func TestWatchBroadcaster_Watch_NameFiltering(t *testing.T) { - b := newWatchBroadcaster[*testItem]() - - // Watcher filtered to "alpha" only - wAlpha := b.Watch("alpha") - defer wAlpha.Stop() - - // Watcher filtered to "beta" only - wBeta := b.Watch("beta") - defer wBeta.Stop() - - // Broadcast event for "alpha" - b.Broadcast(types.Event[*testItem]{Type: types.EventAdded, Object: &testItem{Name: "alpha"}}, "alpha") - - // alpha watcher should receive event - select { - case ev := <-wAlpha.ResultChan(): - assert.Equal(t, "alpha", ev.Object.Name) - case <-time.After(time.Second): - t.Fatal("alpha watcher: timed out waiting for event") - } - - // beta watcher should NOT receive event - select { - case ev := <-wBeta.ResultChan(): - t.Fatalf("beta watcher: unexpected event %v", ev) - case <-time.After(50 * time.Millisecond): - // Expected: no event for beta - } -} - -func TestWatchBroadcaster_Watch_EmptyNameReceivesAll(t *testing.T) { - b := newWatchBroadcaster[*testItem]() - - // Watcher with empty name receives all events - w := b.Watch("") - defer w.Stop() - - b.Broadcast(types.Event[*testItem]{Type: types.EventAdded, Object: &testItem{Name: "alpha"}}, "alpha") - b.Broadcast(types.Event[*testItem]{Type: types.EventAdded, Object: &testItem{Name: "beta"}}, "beta") - - received := make([]string, 0, 2) - for i := 0; i < 2; i++ { - select { - case ev := <-w.ResultChan(): - received = append(received, ev.Object.Name) - case <-time.After(time.Second): - t.Fatal("timed out waiting for event") - } - } - assert.ElementsMatch(t, []string{"alpha", "beta"}, received) -} - -func TestWatchBroadcaster_MultipleWatchers(t *testing.T) { - b := newWatchBroadcaster[*testItem]() - - w1 := b.Watch("") - defer w1.Stop() - w2 := b.Watch("") - defer w2.Stop() - - b.Broadcast(types.Event[*testItem]{Type: types.EventAdded, Object: &testItem{Name: "alpha"}}, "alpha") - - // Both watchers should receive the event - for _, w := range []types.WatchInterface[*testItem]{w1, w2} { - select { - case ev := <-w.ResultChan(): - assert.Equal(t, "alpha", ev.Object.Name) - case <-time.After(time.Second): - t.Fatal("timed out waiting for event") - } - } -} - -func TestWatchBroadcaster_Stop_ClosesChannel(t *testing.T) { - b := newWatchBroadcaster[*testItem]() - - w := b.Watch("") - w.Stop() - - // Channel should be closed after Stop - _, ok := <-w.ResultChan() - assert.False(t, ok, "channel should be closed after Stop") -} - -func TestWatchBroadcaster_Stop_Idempotent(_ *testing.T) { - b := newWatchBroadcaster[*testItem]() - - w := b.Watch("") - - // Multiple stops should not panic - w.Stop() - w.Stop() -} - -func TestWatchBroadcaster_StopAll(t *testing.T) { - b := newWatchBroadcaster[*testItem]() - - w1 := b.Watch("") - w2 := b.Watch("alpha") - - b.StopAll() - - // Both channels should be closed - _, ok1 := <-w1.ResultChan() - assert.False(t, ok1, "w1 channel should be closed after StopAll") - - _, ok2 := <-w2.ResultChan() - assert.False(t, ok2, "w2 channel should be closed after StopAll") -} - -func TestWatchBroadcaster_BroadcastAfterStop_NoDelivery(_ *testing.T) { - b := newWatchBroadcaster[*testItem]() - - w := b.Watch("") - w.Stop() - - // Broadcasting after a watcher stops should not panic - b.Broadcast(types.Event[*testItem]{Type: types.EventAdded, Object: &testItem{Name: "alpha"}}, "alpha") -} - -func TestWatchBroadcaster_StoppedWatcher_RemovedFromBroadcast(t *testing.T) { - b := newWatchBroadcaster[*testItem]() - - w1 := b.Watch("") - w2 := b.Watch("") - - // Stop w1, keep w2 - w1.Stop() - - b.Broadcast(types.Event[*testItem]{Type: types.EventAdded, Object: &testItem{Name: "alpha"}}, "alpha") - - // w2 should still receive events - select { - case ev := <-w2.ResultChan(): - assert.Equal(t, "alpha", ev.Object.Name) - case <-time.After(time.Second): - t.Fatal("timed out waiting for event on w2") - } - - w2.Stop() -} diff --git a/sdk/go/openshell/v1/fake/config.go b/sdk/go/openshell/v1/fake/config.go deleted file mode 100644 index 014d421261..0000000000 --- a/sdk/go/openshell/v1/fake/config.go +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// fakeConfigClient implements v1.ConfigInterface. All methods return -// Unimplemented because configuration management requires a real gateway. -type fakeConfigClient struct { - closedFunc func() bool -} - -// newFakeConfigClient creates a new fakeConfigClient. -func newFakeConfigClient(closedFunc func() bool) *fakeConfigClient { - return &fakeConfigClient{closedFunc: closedFunc} -} - -// GetSandbox returns Unimplemented. -func (c *fakeConfigClient) GetSandbox(_ context.Context, _, sandboxName string) (*types.SandboxConfig, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if sandboxName == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "GetSandbox is not supported by the fake client"} -} - -// GetGateway returns Unimplemented. -func (c *fakeConfigClient) GetGateway(_ context.Context) (*types.GatewayConfig, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "GetGateway is not supported by the fake client"} -} - -// Update returns Unimplemented. A nil update is rejected with InvalidArgument -// to match the real client's behavior. -func (c *fakeConfigClient) Update(_ context.Context, _ string, update *types.ConfigUpdate) (*types.ConfigUpdateResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if update == nil { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "update must not be nil"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Update is not supported by the fake client"} -} - -// Compile-time check that fakeConfigClient implements v1.ConfigInterface. -var _ v1.ConfigInterface = (*fakeConfigClient)(nil) diff --git a/sdk/go/openshell/v1/fake/config_test.go b/sdk/go/openshell/v1/fake/config_test.go deleted file mode 100644 index 54c73af2d3..0000000000 --- a/sdk/go/openshell/v1/fake/config_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- T020: fakeConfigClient stub tests --- - -func TestFakeConfig_GetSandbox_ReturnsUnimplemented(t *testing.T) { - c := newFakeConfigClient(func() bool { return false }) - _, err := c.GetSandbox(context.Background(), "default", "sandbox-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeConfig_GetGateway_ReturnsUnimplemented(t *testing.T) { - c := newFakeConfigClient(func() bool { return false }) - _, err := c.GetGateway(context.Background()) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeConfig_Update_ReturnsUnimplemented(t *testing.T) { - c := newFakeConfigClient(func() bool { return false }) - _, err := c.Update(context.Background(), "default", &types.ConfigUpdate{ - Name: "sandbox-1", - SettingKey: "key", - }) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeConfig_GetSandbox_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeConfigClient(func() bool { return true }) - _, err := c.GetSandbox(context.Background(), "default", "sandbox-1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeConfig_GetGateway_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeConfigClient(func() bool { return true }) - _, err := c.GetGateway(context.Background()) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeConfig_Update_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeConfigClient(func() bool { return true }) - _, err := c.Update(context.Background(), "default", &types.ConfigUpdate{ - Name: "sandbox-1", - SettingKey: "key", - }) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -// --- T033: MergeOperations acceptance test --- - -func TestFakeConfig_Update_MergeOperationsAccepted(t *testing.T) { - c := newFakeConfigClient(func() bool { return false }) - _, err := c.Update(context.Background(), "default", &types.ConfigUpdate{ - Name: "sandbox-1", - MergeOperations: []types.PolicyMergeOperation{{RemoveRule: &types.RemoveNetworkRule{RuleName: "test"}}}, - }) - require.Error(t, err) - // Should return Unimplemented (not InvalidArgument) — MergeOperations are now accepted - assert.True(t, types.IsUnimplemented(err)) - assert.False(t, types.IsInvalidArgument(err)) -} diff --git a/sdk/go/openshell/v1/fake/doc.go b/sdk/go/openshell/v1/fake/doc.go deleted file mode 100644 index 2b17dfcd63..0000000000 --- a/sdk/go/openshell/v1/fake/doc.go +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package fake provides an in-memory fake implementation of the OpenShell SDK -// client interfaces for use in consumer test suites. -// -// The fake client follows the client-go/kubernetes/fake pattern: it maintains -// in-memory stores for sandboxes and providers, supports watch event broadcasting, -// and returns the same StatusError codes as the real client for equivalent error -// conditions (NotFound, AlreadyExists, Unavailable, Unimplemented). -// -// All operations are safe for concurrent use from multiple goroutines. -// -// # Usage -// -// Create a FakeClient, exercise the sandbox lifecycle, and assert results: -// -// func TestSandboxLifecycle(t *testing.T) { -// client := fake.NewClient() -// defer client.Close() -// -// ctx := context.Background() -// -// // Create a sandbox — starts in Provisioning phase -// sb, err := client.Sandboxes().Create(ctx, "my-sandbox", &v1.SandboxSpec{}, nil) -// require.NoError(t, err) -// assert.Equal(t, types.SandboxProvisioning, sb.Status.Phase) -// -// // Wait until ready — transitions synchronously in the fake -// sb, err = client.Sandboxes().WaitReady(ctx, "my-sandbox") -// require.NoError(t, err) -// assert.Equal(t, types.SandboxReady, sb.Status.Phase) -// -// // Clean up -// require.NoError(t, client.Sandboxes().Delete(ctx, "my-sandbox")) -// } -package fake diff --git a/sdk/go/openshell/v1/fake/exec.go b/sdk/go/openshell/v1/fake/exec.go deleted file mode 100644 index 242fbf14ea..0000000000 --- a/sdk/go/openshell/v1/fake/exec.go +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -var _ v1.ExecInterface = (*fakeExecClient)(nil) - -// fakeExecClient implements v1.ExecInterface. All methods return -// Unimplemented because command execution requires a real sandbox runtime. -type fakeExecClient struct { - closedFunc func() bool -} - -// newFakeExecClient creates a new fakeExecClient. -func newFakeExecClient(closedFunc func() bool) *fakeExecClient { - return &fakeExecClient{closedFunc: closedFunc} -} - -// Run returns Unimplemented. -func (c *fakeExecClient) Run(_ context.Context, _, sandboxName string, _ []string, _ ...v1.ExecOptions) (*types.ExecResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if sandboxName == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Run is not supported by the fake client"} -} - -// Stream returns Unimplemented. -func (c *fakeExecClient) Stream(_ context.Context, _, sandboxName string, _ []string, _ ...v1.ExecOptions) (v1.ExecStream, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if sandboxName == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Stream is not supported by the fake client"} -} - -// Interactive returns Unimplemented. -func (c *fakeExecClient) Interactive(_ context.Context, _, sandboxName string, _ []string, _, _ uint32, _ ...v1.ExecOptions) (v1.InteractiveSession, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if sandboxName == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Interactive is not supported by the fake client"} -} diff --git a/sdk/go/openshell/v1/fake/exec_test.go b/sdk/go/openshell/v1/fake/exec_test.go deleted file mode 100644 index bc4163f19e..0000000000 --- a/sdk/go/openshell/v1/fake/exec_test.go +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- T021: Exec stub tests --- - -func TestExec_Run_Unimplemented(t *testing.T) { - ec := newFakeExecClient(func() bool { return false }) - ctx := context.Background() - - _, err := ec.Run(ctx, "default", "sandbox-1", []string{"echo", "hello"}) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestExec_Stream_Unimplemented(t *testing.T) { - ec := newFakeExecClient(func() bool { return false }) - ctx := context.Background() - - _, err := ec.Stream(ctx, "default", "sandbox-1", []string{"tail", "-f", "/var/log/app.log"}) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestExec_Interactive_Unimplemented(t *testing.T) { - ec := newFakeExecClient(func() bool { return false }) - ctx := context.Background() - - _, err := ec.Interactive(ctx, "default", "sandbox-1", []string{"/bin/bash"}, 80, 24) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestExec_Run_ClosedClient(t *testing.T) { - ec := newFakeExecClient(func() bool { return true }) - ctx := context.Background() - - _, err := ec.Run(ctx, "default", "sandbox-1", []string{"echo"}) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestExec_Stream_ClosedClient(t *testing.T) { - ec := newFakeExecClient(func() bool { return true }) - ctx := context.Background() - - _, err := ec.Stream(ctx, "default", "sandbox-1", []string{"tail"}) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestExec_Interactive_ClosedClient(t *testing.T) { - ec := newFakeExecClient(func() bool { return true }) - ctx := context.Background() - - _, err := ec.Interactive(ctx, "default", "sandbox-1", []string{"/bin/bash"}, 80, 24) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} diff --git a/sdk/go/openshell/v1/fake/fake.go b/sdk/go/openshell/v1/fake/fake.go deleted file mode 100644 index 73aa99ec68..0000000000 --- a/sdk/go/openshell/v1/fake/fake.go +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "sync" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// Client implements v1.ClientInterface with in-memory stores. It is -// designed for testing consumers of the OpenShell SDK without requiring a -// real gRPC connection. Create one with NewClient. -type Client struct { - sandboxStore *objectStore[*types.Sandbox] - templateStore *objectStore[*types.SandboxWorkloadTemplate] - providerStore *objectStore[*types.Provider] - workspaceStore *objectStore[*types.Workspace] - memberStore *objectStore[*types.WorkspaceMember] - sandboxBroadcaster *watchBroadcaster[*types.Sandbox] - - sandboxes v1.SandboxInterface - templateCreate v1.SandboxTemplateCreateInterface - suspendResume v1.SandboxSuspendResumeInterface - templates v1.SandboxTemplateInterface - providers v1.ProviderInterface - services v1.ServiceInterface - exec v1.ExecInterface - files v1.FileInterface - health v1.HealthInterface - ssh v1.SSHInterface - tcp v1.TCPInterface - cfg v1.ConfigInterface - policy v1.PolicyInterface - workspaces v1.WorkspaceInterface - - closeOnce sync.Once - closed bool - mu sync.RWMutex // guards closed flag -} - -// ClientOption configures a Client during construction. -type ClientOption func(*Client) - -// WithHealthResult returns an option that configures the health sub-client -// to return the given result instead of the default healthy response. -func WithHealthResult(r *types.HealthResult) ClientOption { - return func(fc *Client) { - fc.health.(*fakeHealthClient).result = r - } -} - -// WithGatewayInfo returns an option that configures the health sub-client -// to return the given gateway info instead of the default response. -func WithGatewayInfo(info *types.GatewayInfo) ClientOption { - return func(fc *Client) { - fc.health.(*fakeHealthClient).gatewayInfo = copyGatewayInfo(info) - } -} - -// WithCurrentUser returns an option that configures the health sub-client -// to return the given current user instead of the default response. -func WithCurrentUser(user *types.CurrentUser) ClientOption { - return func(fc *Client) { - fc.health.(*fakeHealthClient).currentUser = copyCurrentUser(user) - } -} - -// NewClient creates a new Client with all sub-clients wired up. -// Options (e.g., WithHealthResult) are applied after the default setup. -func NewClient(opts ...ClientOption) *Client { - fc := &Client{ - sandboxStore: newobjectStore(sandboxName, copySandbox), - templateStore: newobjectStore(sandboxWorkloadTemplate, copySandboxWorkloadTemplate), - providerStore: newobjectStore(providerName, copyProvider), - workspaceStore: newobjectStore(workspaceName, copyWorkspace), - memberStore: newobjectStore(memberName, copyMember), - sandboxBroadcaster: newWatchBroadcaster[*types.Sandbox](), - } - - sandboxes := newFakeSandboxClient(fc.sandboxStore, fc.templateStore, fc.sandboxBroadcaster, fc.isClosed) - fc.sandboxes = sandboxes - fc.templateCreate = sandboxes - fc.suspendResume = sandboxes - fc.templates = newFakeSandboxTemplateClient(fc.templateStore, fc.isClosed) - fc.providers = newFakeProviderClient(fc.providerStore, fc.isClosed) - fc.services = newFakeServiceClient(fc.isClosed) - fc.exec = newFakeExecClient(fc.isClosed) - fc.files = newFakeFileClient(fc.isClosed) - fc.health = newFakeHealthClient(nil, fc.isClosed) - fc.ssh = newFakeSSHClient(fc.isClosed) - fc.tcp = newFakeTCPClient(fc.isClosed) - fc.cfg = newFakeConfigClient(fc.isClosed) - fc.policy = newFakePolicyClient(fc.isClosed) - fc.workspaces = newFakeWorkspaceClient(fc.workspaceStore, fc.memberStore, fc.isClosed) - - options.Apply(fc, opts) - - return fc -} - -// isClosed returns true if the client has been closed. This is passed to -// all sub-clients as the closedFunc parameter. -func (fc *Client) isClosed() bool { - fc.mu.RLock() - defer fc.mu.RUnlock() - return fc.closed -} - -// Sandboxes returns the sandbox sub-client. -func (fc *Client) Sandboxes() v1.SandboxInterface { return fc.sandboxes } - -// SandboxTemplates returns the reusable sandbox template sub-client. -func (fc *Client) SandboxTemplates() v1.SandboxTemplateInterface { return fc.templates } - -// CreateSandboxFromTemplate creates a sandbox from a named workload template -// without changing the legacy Sandboxes() interface. -func (fc *Client) CreateSandboxFromTemplate(ctx context.Context, workspace, name, templateName string, spec *types.SandboxSpec, opts ...types.CreateOption) (*types.Sandbox, error) { - return fc.templateCreate.CreateFromTemplate(ctx, workspace, name, templateName, spec, opts...) -} - -// SandboxSuspendResume returns the sandbox suspend and resume sub-client. -func (fc *Client) SandboxSuspendResume() v1.SandboxSuspendResumeInterface { - return fc.suspendResume -} - -// Providers returns the provider sub-client. -func (fc *Client) Providers() v1.ProviderInterface { return fc.providers } - -// Services returns the service sub-client. -func (fc *Client) Services() v1.ServiceInterface { return fc.services } - -// Exec returns the exec sub-client. -func (fc *Client) Exec() v1.ExecInterface { return fc.exec } - -// Files returns the file sub-client. -func (fc *Client) Files() v1.FileInterface { return fc.files } - -// Health returns the health sub-client. -func (fc *Client) Health() v1.HealthInterface { return fc.health } - -// SSH returns the SSH session sub-client. -func (fc *Client) SSH() v1.SSHInterface { return fc.ssh } - -// TCP returns the TCP port forwarding sub-client. -func (fc *Client) TCP() v1.TCPInterface { return fc.tcp } - -// Config returns the configuration sub-client. -func (fc *Client) Config() v1.ConfigInterface { return fc.cfg } - -// Policy returns the policy management sub-client. -func (fc *Client) Policy() v1.PolicyInterface { return fc.policy } - -// Workspaces returns the workspace management sub-client. -func (fc *Client) Workspaces() v1.WorkspaceInterface { return fc.workspaces } - -// Close marks the client as closed, stops all active watchers, and causes -// subsequent sub-client calls to return Unavailable. Safe to call multiple -// times. -func (fc *Client) Close() error { - fc.closeOnce.Do(func() { - fc.mu.Lock() - fc.closed = true - fc.mu.Unlock() - - fc.sandboxBroadcaster.StopAll() - }) - return nil -} - -// AddSandbox inserts a sandbox directly into the store without triggering -// watch events. This is intended for pre-seeding test fixtures before the -// test begins. The sandbox is deep-copied on insert. -func (fc *Client) AddSandbox(workspace string, sb *types.Sandbox) { - if sb == nil { - return - } - fc.sandboxStore.Insert(workspace, sb) -} - -// AddSandboxTemplate inserts a sandbox workload template directly into the store. -// This is intended for pre-seeding test fixtures before the test begins. The -// template is deep-copied on insert. -func (fc *Client) AddSandboxTemplate(workspace string, template *types.SandboxWorkloadTemplate) { - if template == nil { - return - } - fc.templateStore.Insert(workspace, template) -} - -// AddProvider inserts a provider directly into the store without triggering -// any side effects. This is intended for pre-seeding test fixtures before -// the test begins. The provider is deep-copied on insert. -func (fc *Client) AddProvider(workspace string, p *types.Provider) { - if p == nil { - return - } - fc.providerStore.Insert(workspace, p) -} - -// AddWorkspace inserts a workspace directly into the store without triggering -// any side effects. This is intended for pre-seeding test fixtures before -// the test begins. The workspace is deep-copied on insert. -func (fc *Client) AddWorkspace(ws *types.Workspace) { - if ws == nil { - return - } - fc.workspaceStore.Insert("", ws) -} - -// AddMember inserts a workspace member directly into the store without -// triggering any side effects. This is intended for pre-seeding test fixtures -// before the test begins. The member is deep-copied on insert. -func (fc *Client) AddMember(workspace string, m *types.WorkspaceMember) { - if m == nil { - return - } - fc.memberStore.Insert(workspace, m) -} - -// AddGlobalRevision adds a gateway-global policy revision for test seeding. -// Global revisions are returned by Policy().List() and Policy().GetStatus() -// when the global option is enabled. -func (fc *Client) AddGlobalRevision(rev types.SandboxPolicyRevision) { - fc.policy.(*fakePolicyClient).AddGlobalRevision(rev) -} - -// AddRevision adds a sandbox-scoped policy revision for test seeding. -// Sandbox revisions are returned by Policy().List() and Policy().GetStatus() -// when the global option is not set. -func (fc *Client) AddRevision(workspace, name string, rev types.SandboxPolicyRevision) { - fc.policy.(*fakePolicyClient).AddRevision(workspace, name, rev) -} - -// Compile-time interface check. -var _ v1.ClientInterface = (*Client)(nil) diff --git a/sdk/go/openshell/v1/fake/fake_test.go b/sdk/go/openshell/v1/fake/fake_test.go deleted file mode 100644 index 12e44badd5..0000000000 --- a/sdk/go/openshell/v1/fake/fake_test.go +++ /dev/null @@ -1,298 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- T025: FakeClient Close tests --- - -func TestFakeClient_Close(t *testing.T) { - fc := NewClient() - - err := fc.Close() - require.NoError(t, err) -} - -func TestFakeClient_Close_Idempotent(t *testing.T) { - fc := NewClient() - - err := fc.Close() - require.NoError(t, err) - - err = fc.Close() - require.NoError(t, err) -} - -func TestFakeClient_Sandboxes_AfterClose(t *testing.T) { - fc := NewClient() - ctx := context.Background() - - _ = fc.Close() - - _, err := fc.Sandboxes().Create(ctx, "default", "test", &types.SandboxSpec{}) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeClient_Providers_AfterClose(t *testing.T) { - fc := NewClient() - ctx := context.Background() - - _ = fc.Close() - - _, err := fc.Providers().Create(ctx, "default", &types.Provider{Name: "test"}) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeClient_Health_AfterClose(t *testing.T) { - fc := NewClient() - ctx := context.Background() - - _ = fc.Close() - - _, err := fc.Health().Check(ctx) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeClient_Exec_AfterClose(t *testing.T) { - fc := NewClient() - ctx := context.Background() - - _ = fc.Close() - - _, err := fc.Exec().Run(ctx, "default", "sandbox", []string{"echo"}) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeClient_Files_AfterClose(t *testing.T) { - fc := NewClient() - ctx := context.Background() - - _ = fc.Close() - - err := fc.Files().Upload(ctx, "default", "sandbox", "/local", "/remote") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeClient_Watch_StoppedOnClose(t *testing.T) { - fc := NewClient() - ctx := context.Background() - - w, err := fc.Sandboxes().Watch(ctx, "default", "") - require.NoError(t, err) - - _ = fc.Close() - - // Channel should be closed after FakeClient.Close - _, ok := <-w.ResultChan() - assert.False(t, ok, "watcher channel should be closed after FakeClient.Close") -} - -func TestFakeClient_WithHealthResult(t *testing.T) { - custom := &types.HealthResult{Healthy: false, Version: "broken"} - fc := NewClient(WithHealthResult(custom)) - ctx := context.Background() - - result, err := fc.Health().Check(ctx) - require.NoError(t, err) - assert.False(t, result.Healthy) - assert.Equal(t, "broken", result.Version) -} - -func TestFakeClient_SubClients(t *testing.T) { - fc := NewClient() - - assert.NotNil(t, fc.Sandboxes()) - assert.NotNil(t, fc.Providers()) - assert.NotNil(t, fc.Exec()) - assert.NotNil(t, fc.Files()) - assert.NotNil(t, fc.Health()) -} - -// --- T013: Pre-seed tests --- - -func TestFakeClient_AddSandbox(t *testing.T) { - fc := NewClient() - ctx := context.Background() - - sb := &types.Sandbox{ - Name: "pre-seeded", - Spec: types.SandboxSpec{LogLevel: "debug"}, - Status: types.SandboxStatus{ - Phase: types.SandboxReady, - }, - } - - fc.AddSandbox("default", sb) - - got, err := fc.Sandboxes().Get(ctx, "default", "pre-seeded") - require.NoError(t, err) - assert.Equal(t, "pre-seeded", got.Name) - assert.Equal(t, "debug", got.Spec.LogLevel) - assert.Equal(t, types.SandboxReady, got.Status.Phase) -} - -func TestFakeClient_AddSandbox_InList(t *testing.T) { - fc := NewClient() - ctx := context.Background() - - fc.AddSandbox("default", &types.Sandbox{Name: "sb-1"}) - fc.AddSandbox("default", &types.Sandbox{Name: "sb-2"}) - - list, err := fc.Sandboxes().ListAll(ctx, "default") - require.NoError(t, err) - assert.Len(t, list, 2) -} - -func TestFakeClient_AddSandbox_NoWatchEvents(t *testing.T) { - fc := NewClient() - ctx := context.Background() - - w, err := fc.Sandboxes().Watch(ctx, "default", "") - require.NoError(t, err) - defer w.Stop() - - fc.AddSandbox("default", &types.Sandbox{Name: "pre-seeded"}) - - // No event should be received — AddSandbox bypasses the broadcaster - select { - case ev := <-w.ResultChan(): - t.Fatalf("unexpected event: %v", ev) - default: - // Good — no event received - } -} - -func TestFakeClient_AddSandbox_DeepCopy(t *testing.T) { - fc := NewClient() - ctx := context.Background() - - sb := &types.Sandbox{ - Name: "pre-seeded", - Labels: map[string]string{"env": "test"}, - } - fc.AddSandbox("default", sb) - - // Mutate the input - sb.Labels["env"] = "mutated" - - got, err := fc.Sandboxes().Get(ctx, "default", "pre-seeded") - require.NoError(t, err) - assert.Equal(t, "test", got.Labels["env"]) -} - -func TestFakeClient_SandboxEndpointStatuses_DeepCopy(t *testing.T) { - fc := NewClient() - ctx := context.Background() - sb := &types.Sandbox{ - Name: "tool-sandbox", - Status: types.SandboxStatus{EndpointStatuses: []types.EndpointStatus{{ - EndpointID: "endpoint-one", - Host: "tools.example.test", - Ports: []uint32{443, 8443}, - Path: "/mcp", - LastResult: types.EndpointTransportFailed, - LastReportedAt: "2026-09-11T10:00:00Z", - }}}, - } - fc.AddSandbox("default", sb) - - // Neither the seed object nor returned snapshots may mutate stored endpoints. - sb.Status.EndpointStatuses[0].Host = "seed-changed.example.test" - sb.Status.EndpointStatuses[0].Ports[0] = 80 - sb.Status.EndpointStatuses[0].LastResult = types.EndpointHTTPResponseReceived - got, err := fc.Sandboxes().Get(ctx, "default", "tool-sandbox") - require.NoError(t, err) - require.Equal(t, []types.EndpointStatus{{ - EndpointID: "endpoint-one", Host: "tools.example.test", Ports: []uint32{443, 8443}, Path: "/mcp", - LastResult: types.EndpointTransportFailed, LastReportedAt: "2026-09-11T10:00:00Z", - }}, got.Status.EndpointStatuses) - got.Status.EndpointStatuses[0].Path = "/changed" - got.Status.EndpointStatuses[0].Ports[1] = 8080 - got.Status.EndpointStatuses[0].LastReportedAt = "changed" - - listed, err := fc.Sandboxes().ListAll(ctx, "default") - require.NoError(t, err) - require.Len(t, listed, 1) - assert.Equal(t, "/mcp", listed[0].Status.EndpointStatuses[0].Path) - assert.Equal(t, []uint32{443, 8443}, listed[0].Status.EndpointStatuses[0].Ports) - assert.Equal(t, "2026-09-11T10:00:00Z", listed[0].Status.EndpointStatuses[0].LastReportedAt) -} - -func TestFakeClient_SandboxGrantFailures_DeepCopy(t *testing.T) { - fc := NewClient() - ctx := context.Background() - upstreamStatus := uint32(401) - failure := &types.EndpointFailure{Code: "provider_authorization_required", UpstreamHTTPStatus: &upstreamStatus} - sb := &types.Sandbox{Name: "grant-sandbox", Status: types.SandboxStatus{ - EndpointStatuses: []types.EndpointStatus{{EndpointID: "endpoint-grant", LastFailure: failure}}, - LastTokenGrantFailure: &types.TokenGrantFailureObservation{DestinationHost: "api.example.test", Failure: failure}, - }} - fc.AddSandbox("default", sb) - - // Neither the seed object nor returned snapshots may mutate the stored failure. - failure.Code = "seed_changed" - upstreamStatus = 500 - got, err := fc.Sandboxes().Get(ctx, "default", "grant-sandbox") - require.NoError(t, err) - got.Status.EndpointStatuses[0].LastFailure.Code = "changed" - got.Status.LastTokenGrantFailure.DestinationHost = "changed.example.test" - *got.Status.LastTokenGrantFailure.Failure.UpstreamHTTPStatus = 503 - - stored, err := fc.Sandboxes().Get(ctx, "default", "grant-sandbox") - require.NoError(t, err) - assert.Equal(t, "provider_authorization_required", stored.Status.EndpointStatuses[0].LastFailure.Code) - assert.Equal(t, uint32(401), *stored.Status.EndpointStatuses[0].LastFailure.UpstreamHTTPStatus) - assert.Equal(t, "api.example.test", stored.Status.LastTokenGrantFailure.DestinationHost) - assert.Equal(t, "provider_authorization_required", stored.Status.LastTokenGrantFailure.Failure.Code) - assert.Equal(t, uint32(401), *stored.Status.LastTokenGrantFailure.Failure.UpstreamHTTPStatus) -} - -func TestFakeClient_AddProvider(t *testing.T) { - fc := NewClient() - ctx := context.Background() - - p := &types.Provider{ - Name: "openai", - Type: "openai", - Spec: types.ProviderSpec{Config: map[string]string{"model": "gpt-4"}}, - } - - fc.AddProvider("default", p) - - got, err := fc.Providers().Get(ctx, "default", "openai") - require.NoError(t, err) - assert.Equal(t, "openai", got.Name) - assert.Equal(t, "gpt-4", got.Spec.Config["model"]) -} - -func TestFakeClient_AddProvider_DeepCopy(t *testing.T) { - fc := NewClient() - ctx := context.Background() - - p := &types.Provider{ - Name: "openai", - Spec: types.ProviderSpec{Config: map[string]string{"model": "gpt-4"}}, - } - fc.AddProvider("default", p) - - // Mutate input - p.Spec.Config["model"] = "mutated" - - got, err := fc.Providers().Get(ctx, "default", "openai") - require.NoError(t, err) - assert.Equal(t, "gpt-4", got.Spec.Config["model"]) -} diff --git a/sdk/go/openshell/v1/fake/file.go b/sdk/go/openshell/v1/fake/file.go deleted file mode 100644 index 52b234219d..0000000000 --- a/sdk/go/openshell/v1/fake/file.go +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -var _ v1.FileInterface = (*fakeFileClient)(nil) - -// fakeFileClient implements v1.FileInterface. All methods return -// Unimplemented because file transfer requires a real sandbox runtime. -type fakeFileClient struct { - closedFunc func() bool -} - -// newFakeFileClient creates a new fakeFileClient. -func newFakeFileClient(closedFunc func() bool) *fakeFileClient { - return &fakeFileClient{closedFunc: closedFunc} -} - -// Upload returns Unimplemented. -func (c *fakeFileClient) Upload(_ context.Context, _, sandboxName, _, remotePath string) error { - if c.closedFunc() { - return &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if sandboxName == "" { - return &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - if remotePath == "" { - return &types.StatusError{Code: types.ErrorInvalidArgument, Message: "remote path must not be empty"} - } - return &types.StatusError{Code: types.ErrorUnimplemented, Message: "Upload is not supported by the fake client"} -} - -// Download returns Unimplemented. -func (c *fakeFileClient) Download(_ context.Context, _, sandboxName, remotePath, _ string) error { - if c.closedFunc() { - return &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if sandboxName == "" { - return &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - if remotePath == "" { - return &types.StatusError{Code: types.ErrorInvalidArgument, Message: "remote path must not be empty"} - } - return &types.StatusError{Code: types.ErrorUnimplemented, Message: "Download is not supported by the fake client"} -} diff --git a/sdk/go/openshell/v1/fake/file_test.go b/sdk/go/openshell/v1/fake/file_test.go deleted file mode 100644 index 05a991bfea..0000000000 --- a/sdk/go/openshell/v1/fake/file_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- T022: File stub tests --- - -func TestFile_Upload_Unimplemented(t *testing.T) { - fc := newFakeFileClient(func() bool { return false }) - ctx := context.Background() - - err := fc.Upload(ctx, "default", "test-sandbox", "/local/file.txt", "/remote/file.txt") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFile_Download_Unimplemented(t *testing.T) { - fc := newFakeFileClient(func() bool { return false }) - ctx := context.Background() - - err := fc.Download(ctx, "default", "test-sandbox", "/remote/file.txt", "/local/file.txt") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFile_Upload_ClosedClient(t *testing.T) { - fc := newFakeFileClient(func() bool { return true }) - ctx := context.Background() - - err := fc.Upload(ctx, "default", "test-sandbox", "/local/file.txt", "/remote/file.txt") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFile_Download_ClosedClient(t *testing.T) { - fc := newFakeFileClient(func() bool { return true }) - ctx := context.Background() - - err := fc.Download(ctx, "default", "test-sandbox", "/remote/file.txt", "/local/file.txt") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} diff --git a/sdk/go/openshell/v1/fake/health.go b/sdk/go/openshell/v1/fake/health.go deleted file mode 100644 index 304107a50f..0000000000 --- a/sdk/go/openshell/v1/fake/health.go +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// fakeHealthClient implements v1.HealthInterface with configurable -// responses. When no custom result is provided, Check returns a -// default healthy response. -type fakeHealthClient struct { - result *types.HealthResult - gatewayInfo *types.GatewayInfo - currentUser *types.CurrentUser - closedFunc func() bool -} - -// newFakeHealthClient creates a new fakeHealthClient. If result is nil, -// Check will return the default healthy response. -func newFakeHealthClient(result *types.HealthResult, closedFunc func() bool) *fakeHealthClient { - return &fakeHealthClient{ - result: result, - closedFunc: closedFunc, - } -} - -// Check returns the configured health result. If no custom result was -// provided, it returns {Healthy: true, Version: "fake"}. -func (c *fakeHealthClient) Check(_ context.Context) (*types.HealthResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - if c.result != nil { - cp := *c.result - return &cp, nil - } - - return &types.HealthResult{ - Healthy: true, - Version: "fake", - }, nil -} - -// GetGatewayInfo returns the configured gateway info. If no custom info -// was provided, it returns a default healthy response. -func (c *fakeHealthClient) GetGatewayInfo(_ context.Context) (*types.GatewayInfo, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - if c.gatewayInfo != nil { - return copyGatewayInfo(c.gatewayInfo), nil - } - - return &types.GatewayInfo{ - Status: types.ServiceStatusHealthy, - Version: "fake", - }, nil -} - -// GetCurrentUser returns the configured current user. If no custom user -// was provided, it returns a default user. -func (c *fakeHealthClient) GetCurrentUser(_ context.Context) (*types.CurrentUser, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - if c.currentUser != nil { - return copyCurrentUser(c.currentUser), nil - } - - return &types.CurrentUser{ - Subject: "fake-user", - DisplayName: "Fake User", - }, nil -} - -func copyGatewayInfo(info *types.GatewayInfo) *types.GatewayInfo { - if info == nil { - return nil - } - cp := *info - if info.ComputeDrivers != nil { - cp.ComputeDrivers = make([]types.ComputeDriverInfo, len(info.ComputeDrivers)) - copy(cp.ComputeDrivers, info.ComputeDrivers) - } - if info.Extensions != nil { - cp.Extensions = make([]types.ExtensionInfo, len(info.Extensions)) - for index, extension := range info.Extensions { - cp.Extensions[index] = extension - cp.Extensions[index].SupportedCapabilities = copyStringSlice(extension.SupportedCapabilities) - cp.Extensions[index].RequiredCapabilities = copyStringSlice(extension.RequiredCapabilities) - } - } - return &cp -} - -func copyCurrentUser(user *types.CurrentUser) *types.CurrentUser { - if user == nil { - return nil - } - cp := *user - cp.Roles = copyStringSlice(user.Roles) - cp.Scopes = copyStringSlice(user.Scopes) - return &cp -} diff --git a/sdk/go/openshell/v1/fake/health_test.go b/sdk/go/openshell/v1/fake/health_test.go deleted file mode 100644 index 63656ba3f6..0000000000 --- a/sdk/go/openshell/v1/fake/health_test.go +++ /dev/null @@ -1,161 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- T017: Health check tests --- - -func TestHealth_DefaultHealthy(t *testing.T) { - hc := newFakeHealthClient(nil, func() bool { return false }) - ctx := context.Background() - - result, err := hc.Check(ctx) - require.NoError(t, err) - assert.True(t, result.Healthy) - assert.Equal(t, "fake", result.Version) -} - -func TestHealth_ConfigurableResult(t *testing.T) { - custom := &types.HealthResult{ - Healthy: false, - Version: "v0.0.0-broken", - } - hc := newFakeHealthClient(custom, func() bool { return false }) - ctx := context.Background() - - result, err := hc.Check(ctx) - require.NoError(t, err) - assert.False(t, result.Healthy) - assert.Equal(t, "v0.0.0-broken", result.Version) -} - -func TestHealth_ClosedClient(t *testing.T) { - hc := newFakeHealthClient(nil, func() bool { return true }) - ctx := context.Background() - - _, err := hc.Check(ctx) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestHealth_GetGatewayInfo_Default(t *testing.T) { - fc := NewClient() - info, err := fc.Health().GetGatewayInfo(context.Background()) - - require.NoError(t, err) - require.NotNil(t, info) - assert.Equal(t, types.ServiceStatusHealthy, info.Status) - assert.Equal(t, "fake", info.Version) -} - -func TestHealth_GetGatewayInfo_Custom(t *testing.T) { - fc := NewClient(WithGatewayInfo(&types.GatewayInfo{ - Status: types.ServiceStatusDegraded, - Version: "1.2.3", - ComputeDrivers: []types.ComputeDriverInfo{ - {Name: "k8s", DriverName: "kubernetes", DriverVersion: "2.0.0"}, - }, - })) - - info, err := fc.Health().GetGatewayInfo(context.Background()) - - require.NoError(t, err) - require.NotNil(t, info) - assert.Equal(t, types.ServiceStatusDegraded, info.Status) - assert.Equal(t, "1.2.3", info.Version) - require.Len(t, info.ComputeDrivers, 1) - assert.Equal(t, "k8s", info.ComputeDrivers[0].Name) -} - -func TestHealth_GetGatewayInfo_DeepCopy(t *testing.T) { - fc := NewClient(WithGatewayInfo(&types.GatewayInfo{ - Status: types.ServiceStatusHealthy, - Version: "1.0.0", - ComputeDrivers: []types.ComputeDriverInfo{ - {Name: "k8s"}, - }, - Extensions: []types.ExtensionInfo{{ - ConfiguredName: "k8s", - SupportedCapabilities: []string{"openshell.compute.contract"}, - }}, - })) - - info1, _ := fc.Health().GetGatewayInfo(context.Background()) - info1.ComputeDrivers[0].Name = "mutated" - info1.Extensions[0].ConfiguredName = "mutated" - info1.Extensions[0].SupportedCapabilities[0] = "mutated" - - info2, _ := fc.Health().GetGatewayInfo(context.Background()) - assert.Equal(t, "k8s", info2.ComputeDrivers[0].Name) - assert.Equal(t, "k8s", info2.Extensions[0].ConfiguredName) - assert.Equal(t, "openshell.compute.contract", info2.Extensions[0].SupportedCapabilities[0]) -} - -func TestHealth_GetCurrentUser_Default(t *testing.T) { - fc := NewClient() - user, err := fc.Health().GetCurrentUser(context.Background()) - - require.NoError(t, err) - require.NotNil(t, user) - assert.Equal(t, "fake-user", user.Subject) - assert.Equal(t, "Fake User", user.DisplayName) -} - -func TestHealth_GetCurrentUser_Custom(t *testing.T) { - fc := NewClient(WithCurrentUser(&types.CurrentUser{ - Subject: "real-user", - DisplayName: "Real User", - Roles: []string{"admin"}, - Scopes: []string{"read", "write"}, - IdentityProvider: "oidc", - })) - - user, err := fc.Health().GetCurrentUser(context.Background()) - - require.NoError(t, err) - require.NotNil(t, user) - assert.Equal(t, "real-user", user.Subject) - assert.Equal(t, "Real User", user.DisplayName) - assert.Equal(t, []string{"admin"}, user.Roles) - assert.Equal(t, []string{"read", "write"}, user.Scopes) - assert.Equal(t, "oidc", user.IdentityProvider) -} - -func TestHealth_GetCurrentUser_DeepCopy(t *testing.T) { - fc := NewClient(WithCurrentUser(&types.CurrentUser{ - Subject: "user", - Roles: []string{"admin"}, - })) - - user1, _ := fc.Health().GetCurrentUser(context.Background()) - user1.Roles[0] = "mutated" - - user2, _ := fc.Health().GetCurrentUser(context.Background()) - assert.Equal(t, "admin", user2.Roles[0]) -} - -func TestHealth_GetGatewayInfo_Closed(t *testing.T) { - fc := NewClient() - _ = fc.Close() - - _, err := fc.Health().GetGatewayInfo(context.Background()) - assert.True(t, types.IsUnavailable(err)) -} - -func TestHealth_GetCurrentUser_Closed(t *testing.T) { - fc := NewClient() - _ = fc.Close() - - _, err := fc.Health().GetCurrentUser(context.Background()) - assert.True(t, types.IsUnavailable(err)) -} diff --git a/sdk/go/openshell/v1/fake/mutations.go b/sdk/go/openshell/v1/fake/mutations.go deleted file mode 100644 index 2f69ccf326..0000000000 --- a/sdk/go/openshell/v1/fake/mutations.go +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -func deletionResult(existed bool, sandboxID string, opts []v1.DeleteOptions) (*types.DeletionResult, error) { - if existed { - return &types.DeletionResult{Outcome: types.DeletionCompleted, SandboxID: sandboxID}, nil - } - if len(opts) > 0 && opts[0].AllowMissing { - return &types.DeletionResult{Outcome: types.DeletionAlreadyAbsent}, nil - } - return nil, &types.StatusError{Code: types.ErrorNotFound, Message: "target not found"} -} diff --git a/sdk/go/openshell/v1/fake/options_nil_test.go b/sdk/go/openshell/v1/fake/options_nil_test.go deleted file mode 100644 index b58fcdd8e9..0000000000 --- a/sdk/go/openshell/v1/fake/options_nil_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -func TestClientOption_NilIgnored(t *testing.T) { - hr := &types.HealthResult{Healthy: false, Version: "1.2.3"} - cu := &types.CurrentUser{Subject: "user-42", DisplayName: "Test User"} - - fc := NewClient( - WithHealthResult(hr), - nil, - WithCurrentUser(cu), - ) - require.NotNil(t, fc) - - healthClient := fc.health.(*fakeHealthClient) - assert.Equal(t, false, healthClient.result.Healthy) - assert.Equal(t, "1.2.3", healthClient.result.Version) - assert.Equal(t, "Test User", healthClient.currentUser.DisplayName) -} diff --git a/sdk/go/openshell/v1/fake/pager.go b/sdk/go/openshell/v1/fake/pager.go deleted file mode 100644 index fed00135d5..0000000000 --- a/sdk/go/openshell/v1/fake/pager.go +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "strconv" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -func newSlicePager[T any](items []T, pageSize int, pageToken string) (*v1.Pager[T], error) { - if pageSize < 0 { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "page size must not be negative"} - } - if pageSize == 0 { - pageSize = 100 - } - if _, err := parsePageOffset(pageToken, len(items)); err != nil { - return nil, err - } - return v1.NewPager(pageToken, func(_ context.Context, token string) (*v1.Page[T], error) { - start, err := parsePageOffset(token, len(items)) - if err != nil { - return nil, err - } - end := min(start+pageSize, len(items)) - next := "" - if end < len(items) { - next = strconv.Itoa(end) - } - pageItems := append(make([]T, 0, end-start), items[start:end]...) - return &v1.Page[T]{Items: pageItems, NextPageToken: next}, nil - }), nil -} - -func parsePageOffset(token string, length int) (int, error) { - if token == "" { - return 0, nil - } - offset, err := strconv.Atoi(token) - if err != nil || offset < 0 || offset > length { - return 0, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "invalid page token"} - } - return offset, nil -} diff --git a/sdk/go/openshell/v1/fake/policy.go b/sdk/go/openshell/v1/fake/policy.go deleted file mode 100644 index 49c62cda92..0000000000 --- a/sdk/go/openshell/v1/fake/policy.go +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "maps" - "slices" - "sync" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -func copySandboxPolicyRevision(r types.SandboxPolicyRevision) types.SandboxPolicyRevision { - if r.Policy != nil { - cp := *r.Policy - if r.Policy.NetworkPolicies != nil { - cp.NetworkPolicies = make(map[string]types.NetworkPolicyRule, len(r.Policy.NetworkPolicies)) - maps.Copy(cp.NetworkPolicies, r.Policy.NetworkPolicies) - } - r.Policy = &cp - } - return r -} - -// fakePolicyClient implements v1.PolicyInterface. List and GetStatus support -// in-memory global and sandbox-scoped revisions. Other methods return -// Unimplemented because policy management requires a real gateway. -type fakePolicyClient struct { - mu sync.RWMutex - closedFunc func() bool - - // globalRevisions stores gateway-global policy revisions. - globalRevisions []types.SandboxPolicyRevision - // sandboxRevisions stores sandbox-scoped revisions keyed by "workspace/name". - sandboxRevisions map[string][]types.SandboxPolicyRevision -} - -// newFakePolicyClient creates a new fakePolicyClient. -func newFakePolicyClient(closedFunc func() bool) *fakePolicyClient { - return &fakePolicyClient{ - closedFunc: closedFunc, - sandboxRevisions: make(map[string][]types.SandboxPolicyRevision), - } -} - -// AddGlobalRevision adds a global policy revision for test seeding. -func (c *fakePolicyClient) AddGlobalRevision(rev types.SandboxPolicyRevision) { - c.mu.Lock() - defer c.mu.Unlock() - c.globalRevisions = append(c.globalRevisions, copySandboxPolicyRevision(rev)) -} - -// AddRevision adds a sandbox-scoped policy revision for test seeding. -func (c *fakePolicyClient) AddRevision(workspace, name string, rev types.SandboxPolicyRevision) { - c.mu.Lock() - defer c.mu.Unlock() - key := workspace + "/" + name - c.sandboxRevisions[key] = append(c.sandboxRevisions[key], copySandboxPolicyRevision(rev)) -} - -// GetDraft returns Unimplemented. -func (c *fakePolicyClient) GetDraft(_ context.Context, _, _ string, _ ...v1.GetDraftOption) (*types.DraftPolicy, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "GetDraft is not supported by the fake client"} -} - -// ApproveDraftChunk returns Unimplemented. -func (c *fakePolicyClient) ApproveDraftChunk(_ context.Context, _, _, _, _ string) (*types.ApproveResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "ApproveDraftChunk is not supported by the fake client"} -} - -// RejectDraftChunk returns Unimplemented. -func (c *fakePolicyClient) RejectDraftChunk(_ context.Context, _, _, _, _ string) error { - if c.closedFunc() { - return &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return &types.StatusError{Code: types.ErrorUnimplemented, Message: "RejectDraftChunk is not supported by the fake client"} -} - -// ApproveAllDraftChunks returns Unimplemented. -func (c *fakePolicyClient) ApproveAllDraftChunks(_ context.Context, _, _ string, _ ...v1.ApproveAllOption) (*types.ApproveAllResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "ApproveAllDraftChunks is not supported by the fake client"} -} - -// ClearDraftChunks returns Unimplemented. -func (c *fakePolicyClient) ClearDraftChunks(_ context.Context, _, _ string) (*types.ClearResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "ClearDraftChunks is not supported by the fake client"} -} - -// GetDraftHistory returns Unimplemented. -func (c *fakePolicyClient) GetDraftHistory(_ context.Context, _, _ string) ([]types.DraftHistoryEntry, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "GetDraftHistory is not supported by the fake client"} -} - -// GetStatus returns the status of a policy revision. When the global option is -// set, it queries global revisions; otherwise it queries sandbox-scoped ones. -func (c *fakePolicyClient) GetStatus(_ context.Context, workspace, sandboxName string, opts ...v1.GetStatusOption) (*types.PolicyStatusResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - cfg := types.ApplyGetStatusOptions(opts) - - c.mu.RLock() - defer c.mu.RUnlock() - - var revisions []types.SandboxPolicyRevision - if cfg.Global() { - revisions = c.globalRevisions - } else { - key := workspace + "/" + sandboxName - revisions = c.sandboxRevisions[key] - } - - if len(revisions) == 0 { - return nil, &types.StatusError{Code: types.ErrorNotFound, Message: "no policy revisions found"} - } - - // Find the active version (highest version with Loaded status). - var activeVersion uint32 - for _, r := range revisions { - if r.Status == types.PolicyLoadStatusLoaded && r.Version > activeVersion { - activeVersion = r.Version - } - } - // If no loaded version, use the highest version. - var maxVersion uint32 - var maxIdx int - for i, r := range revisions { - if r.Version > maxVersion { - maxVersion = r.Version - maxIdx = i - } - } - if activeVersion == 0 { - activeVersion = maxVersion - } - - // Find the requested revision. - targetVersion := cfg.Version() - if targetVersion == 0 { - // Latest revision (by highest version, not insertion order). - rev := copySandboxPolicyRevision(revisions[maxIdx]) - return &types.PolicyStatusResult{Revision: rev, ActiveVersion: activeVersion}, nil - } - - for _, r := range revisions { - if r.Version == targetVersion { - rev := copySandboxPolicyRevision(r) - return &types.PolicyStatusResult{Revision: rev, ActiveVersion: activeVersion}, nil - } - } - - return nil, &types.StatusError{Code: types.ErrorNotFound, Message: "policy version not found"} -} - -// List returns policy revisions. When the global option is set, it returns -// global revisions; otherwise it returns revisions for the specified sandbox. -func (c *fakePolicyClient) List(workspace, sandboxName string, opts ...v1.ListPolicyOption) (*v1.Pager[types.SandboxPolicyRevision], error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - cfg := types.ApplyListPolicyOptions(opts) - if cfg.PageSize() < 0 { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "page size must not be negative"} - } - if !cfg.Global() && sandboxName == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - - c.mu.RLock() - defer c.mu.RUnlock() - - var revisions []types.SandboxPolicyRevision - if cfg.Global() { - revisions = slices.Clone(c.globalRevisions) - } else { - revisions = slices.Clone(c.sandboxRevisions[workspace+"/"+sandboxName]) - } - - // Sort by version for deterministic ordering (map iteration is random). - slices.SortFunc(revisions, func(a, b types.SandboxPolicyRevision) int { - if a.Version < b.Version { - return -1 - } - if a.Version > b.Version { - return 1 - } - return 0 - }) - - result := make([]types.SandboxPolicyRevision, len(revisions)) - for i, r := range revisions { - result[i] = copySandboxPolicyRevision(r) - } - return newSlicePager(result, int(cfg.PageSize()), cfg.PageToken()) -} - -func (c *fakePolicyClient) ListAll(ctx context.Context, workspace, sandboxName string, opts ...v1.ListPolicyOption) ([]types.SandboxPolicyRevision, error) { - pager, err := c.List(workspace, sandboxName, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -// EditDraftChunk returns Unimplemented. -func (c *fakePolicyClient) EditDraftChunk(_ context.Context, _, _, _ string, _ *types.NetworkPolicyRule) error { - if c.closedFunc() { - return &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return &types.StatusError{Code: types.ErrorUnimplemented, Message: "EditDraftChunk is not supported by the fake client"} -} - -// UndoDraftChunk returns Unimplemented. -func (c *fakePolicyClient) UndoDraftChunk(_ context.Context, _, _, _ string) (*types.UndoResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "UndoDraftChunk is not supported by the fake client"} -} - -// Compile-time check that fakePolicyClient implements v1.PolicyInterface. -var _ v1.PolicyInterface = (*fakePolicyClient)(nil) diff --git a/sdk/go/openshell/v1/fake/policy_test.go b/sdk/go/openshell/v1/fake/policy_test.go deleted file mode 100644 index 924bd2fab6..0000000000 --- a/sdk/go/openshell/v1/fake/policy_test.go +++ /dev/null @@ -1,287 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- T031: fakePolicyClient stub tests --- - -func TestFakePolicy_GetDraft_ReturnsUnimplemented(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - _, err := c.GetDraft(context.Background(), "default", "sb-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakePolicy_ApproveDraftChunk_ReturnsUnimplemented(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - _, err := c.ApproveDraftChunk(context.Background(), "default", "sb-1", "chunk-1", "token-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakePolicy_RejectDraftChunk_ReturnsUnimplemented(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - err := c.RejectDraftChunk(context.Background(), "default", "sb-1", "chunk-1", "bad rule") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakePolicy_ApproveAllDraftChunks_ReturnsUnimplemented(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - _, err := c.ApproveAllDraftChunks(context.Background(), "default", "sb-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakePolicy_ClearDraftChunks_ReturnsUnimplemented(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - _, err := c.ClearDraftChunks(context.Background(), "default", "sb-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakePolicy_GetDraftHistory_ReturnsUnimplemented(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - _, err := c.GetDraftHistory(context.Background(), "default", "sb-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakePolicy_GetStatus_EmptyReturnsNotFound(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - _, err := c.GetStatus(context.Background(), "default", "sb-1") - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestFakePolicy_List_EmptyReturnsNonNil(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - revisions, err := c.ListAll(context.Background(), "default", "sb-1") - require.NoError(t, err) - assert.NotNil(t, revisions) - assert.Empty(t, revisions) -} - -func TestFakePolicy_EditDraftChunk_ReturnsUnimplemented(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - err := c.EditDraftChunk(context.Background(), "default", "sb-1", "chunk-1", &types.NetworkPolicyRule{Name: "test"}) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakePolicy_UndoDraftChunk_ReturnsUnimplemented(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - _, err := c.UndoDraftChunk(context.Background(), "default", "sb-1", "chunk-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -// --- T007: Global policy List and GetStatus tests --- - -func TestFakePolicy_List_Global(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - - // Seed global and sandbox-scoped revisions. - c.AddGlobalRevision(types.SandboxPolicyRevision{Version: 1, PolicyHash: "sha256:global-v1", Status: types.PolicyLoadStatusLoaded}) - c.AddGlobalRevision(types.SandboxPolicyRevision{Version: 2, PolicyHash: "sha256:global-v2", Status: types.PolicyLoadStatusPending}) - c.AddRevision("default", "sb-1", types.SandboxPolicyRevision{Version: 1, PolicyHash: "sha256:sb-v1", Status: types.PolicyLoadStatusLoaded}) - - // List global revisions. - revisions, err := c.ListAll(context.Background(), "", "", types.WithListGlobal(true)) - require.NoError(t, err) - require.Len(t, revisions, 2) - assert.Equal(t, uint32(1), revisions[0].Version) - assert.Equal(t, "sha256:global-v1", revisions[0].PolicyHash) - assert.Equal(t, uint32(2), revisions[1].Version) -} - -func TestFakePolicy_List_Sandbox(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - - // Seed global and sandbox-scoped revisions. - c.AddGlobalRevision(types.SandboxPolicyRevision{Version: 1, PolicyHash: "sha256:global-v1"}) - c.AddRevision("default", "sb-1", types.SandboxPolicyRevision{Version: 1, PolicyHash: "sha256:sb-v1", Status: types.PolicyLoadStatusLoaded}) - c.AddRevision("default", "sb-1", types.SandboxPolicyRevision{Version: 2, PolicyHash: "sha256:sb-v2", Status: types.PolicyLoadStatusPending}) - - // List sandbox-scoped revisions (no global flag). - revisions, err := c.ListAll(context.Background(), "default", "sb-1") - require.NoError(t, err) - require.Len(t, revisions, 2) - assert.Equal(t, "sha256:sb-v1", revisions[0].PolicyHash) - assert.Equal(t, "sha256:sb-v2", revisions[1].PolicyHash) -} - -func TestFakePolicy_List_NoIsolationCrossContamination(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - - // Only seed sandbox-scoped revisions. - c.AddRevision("default", "sb-1", types.SandboxPolicyRevision{Version: 1, PolicyHash: "sha256:sb-v1"}) - - // Global list returns empty (no global revisions seeded). - revisions, err := c.ListAll(context.Background(), "", "", types.WithListGlobal(true)) - require.NoError(t, err) - assert.NotNil(t, revisions) - assert.Empty(t, revisions) -} - -func TestFakePolicy_List_GlobalPageSizeStillReturnsAll(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - - c.AddGlobalRevision(types.SandboxPolicyRevision{Version: 1}) - c.AddGlobalRevision(types.SandboxPolicyRevision{Version: 2}) - c.AddGlobalRevision(types.SandboxPolicyRevision{Version: 3}) - - revisions, err := c.ListAll(context.Background(), "", "", types.WithListGlobal(true), types.WithPageSize(2)) - require.NoError(t, err) - require.Len(t, revisions, 3) - assert.Equal(t, uint32(1), revisions[0].Version) - assert.Equal(t, uint32(2), revisions[1].Version) - assert.Equal(t, uint32(3), revisions[2].Version) -} - -func TestFakePolicy_GetStatus_Sandbox(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - - c.AddRevision("default", "sb-1", types.SandboxPolicyRevision{Version: 1, PolicyHash: "sha256:sb-v1", Status: types.PolicyLoadStatusLoaded}) - c.AddRevision("default", "sb-1", types.SandboxPolicyRevision{Version: 2, PolicyHash: "sha256:sb-v2", Status: types.PolicyLoadStatusPending}) - - result, err := c.GetStatus(context.Background(), "default", "sb-1") - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, uint32(2), result.Revision.Version) - assert.Equal(t, "sha256:sb-v2", result.Revision.PolicyHash) - assert.Equal(t, uint32(1), result.ActiveVersion) -} - -func TestFakePolicy_GetStatus_Global(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - - c.AddGlobalRevision(types.SandboxPolicyRevision{Version: 1, PolicyHash: "sha256:global-v1", Status: types.PolicyLoadStatusSuperseded}) - c.AddGlobalRevision(types.SandboxPolicyRevision{Version: 2, PolicyHash: "sha256:global-v2", Status: types.PolicyLoadStatusLoaded}) - - // Get global status (latest). - result, err := c.GetStatus(context.Background(), "", "", types.WithStatusGlobal(true)) - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, uint32(2), result.Revision.Version) - assert.Equal(t, "sha256:global-v2", result.Revision.PolicyHash) - assert.Equal(t, uint32(2), result.ActiveVersion) -} - -func TestFakePolicy_GetStatus_GlobalWithVersion(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - - c.AddGlobalRevision(types.SandboxPolicyRevision{Version: 1, PolicyHash: "sha256:global-v1", Status: types.PolicyLoadStatusSuperseded}) - c.AddGlobalRevision(types.SandboxPolicyRevision{Version: 2, PolicyHash: "sha256:global-v2", Status: types.PolicyLoadStatusLoaded}) - - // Get specific global version. - result, err := c.GetStatus(context.Background(), "", "", types.WithStatusGlobal(true), types.WithVersion(1)) - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, uint32(1), result.Revision.Version) - assert.Equal(t, types.PolicyLoadStatusSuperseded, result.Revision.Status) - assert.Equal(t, uint32(2), result.ActiveVersion) -} - -func TestFakePolicy_GetStatus_GlobalNotFound(t *testing.T) { - c := newFakePolicyClient(func() bool { return false }) - - // No global revisions seeded. - _, err := c.GetStatus(context.Background(), "", "", types.WithStatusGlobal(true)) - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestFakePolicy_DeepCopyWithPolicy(t *testing.T) { - fc := NewClient() - defer fc.Close() //nolint:errcheck - - fc.AddGlobalRevision(types.SandboxPolicyRevision{ - Version: 1, - PolicyHash: "sha256:with-policy", - Status: types.PolicyLoadStatusLoaded, - Policy: &types.SandboxPolicy{ - NetworkPolicies: map[string]types.NetworkPolicyRule{ - "rule-1": {Name: "rule-1"}, - }, - }, - }) - - fc.AddRevision("default", "sb-1", types.SandboxPolicyRevision{ - Version: 1, - PolicyHash: "sha256:sb-policy", - Status: types.PolicyLoadStatusLoaded, - Policy: &types.SandboxPolicy{ - NetworkPolicies: map[string]types.NetworkPolicyRule{ - "rule-2": {Name: "rule-2"}, - }, - }, - }) - - ctx := context.Background() - - // Get global revision and mutate it. - revisions, err := fc.Policy().ListAll(ctx, "", "", types.WithListGlobal(true)) - require.NoError(t, err) - require.Len(t, revisions, 1) - require.NotNil(t, revisions[0].Policy) - revisions[0].Policy.NetworkPolicies["rule-1"] = types.NetworkPolicyRule{Name: "mutated"} - - // Verify internal state is not corrupted. - revisions2, err := fc.Policy().ListAll(ctx, "", "", types.WithListGlobal(true)) - require.NoError(t, err) - assert.Equal(t, "rule-1", revisions2[0].Policy.NetworkPolicies["rule-1"].Name) - - // Get sandbox revision via GetStatus and verify deep copy. - status, err := fc.Policy().GetStatus(ctx, "default", "sb-1") - require.NoError(t, err) - require.NotNil(t, status.Revision.Policy) - assert.Equal(t, "rule-2", status.Revision.Policy.NetworkPolicies["rule-2"].Name) -} - -func TestFakePolicy_List_ClosedReturnsUnavailable(t *testing.T) { - c := newFakePolicyClient(func() bool { return true }) - _, err := c.ListAll(context.Background(), "", "", types.WithListGlobal(true)) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakePolicy_GetStatus_ClosedReturnsUnavailable(t *testing.T) { - c := newFakePolicyClient(func() bool { return true }) - _, err := c.GetStatus(context.Background(), "", "", types.WithStatusGlobal(true)) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -// --- Closed client tests --- - -func TestFakePolicy_GetDraft_ClosedReturnsUnavailable(t *testing.T) { - c := newFakePolicyClient(func() bool { return true }) - _, err := c.GetDraft(context.Background(), "default", "sb-1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakePolicy_ApproveDraftChunk_ClosedReturnsUnavailable(t *testing.T) { - c := newFakePolicyClient(func() bool { return true }) - _, err := c.ApproveDraftChunk(context.Background(), "default", "sb-1", "chunk-1", "token-1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakePolicy_RejectDraftChunk_ClosedReturnsUnavailable(t *testing.T) { - c := newFakePolicyClient(func() bool { return true }) - err := c.RejectDraftChunk(context.Background(), "default", "sb-1", "chunk-1", "reason") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} diff --git a/sdk/go/openshell/v1/fake/profile.go b/sdk/go/openshell/v1/fake/profile.go deleted file mode 100644 index d39d65dbcd..0000000000 --- a/sdk/go/openshell/v1/fake/profile.go +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// fakeProfileClient implements v1.ProfileInterface. All methods return -// Unimplemented because profile management requires a real server. -type fakeProfileClient struct { - closedFunc func() bool -} - -// newFakeProfileClient creates a new fakeProfileClient. -func newFakeProfileClient(closedFunc func() bool) *fakeProfileClient { - return &fakeProfileClient{closedFunc: closedFunc} -} - -// List returns Unimplemented. -func (c *fakeProfileClient) List(_ string, _ ...v1.ListOptions) (*v1.Pager[*types.ProviderProfile], error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "List is not supported by the fake client"} -} - -// ListAll returns Unimplemented. -func (c *fakeProfileClient) ListAll(_ context.Context, _ string, _ ...v1.ListOptions) ([]*types.ProviderProfile, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "ListAll is not supported by the fake client"} -} - -// Get returns Unimplemented. -func (c *fakeProfileClient) Get(_ context.Context, _, _ string) (*types.ProviderProfile, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Get is not supported by the fake client"} -} - -// Import returns Unimplemented. -func (c *fakeProfileClient) Import(_ context.Context, _ string, _ []types.ProfileImportItem) (*types.ImportResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Import is not supported by the fake client"} -} - -// Update returns Unimplemented. -func (c *fakeProfileClient) Update(_ context.Context, _, _ string, _ uint64, _ types.ProfileImportItem) (*types.UpdateResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Update is not supported by the fake client"} -} - -// Lint returns Unimplemented. -func (c *fakeProfileClient) Lint(_ context.Context, _ string, _ []types.ProfileImportItem) (*types.LintResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Lint is not supported by the fake client"} -} - -// Delete returns Unimplemented. -func (c *fakeProfileClient) Delete(_ context.Context, _, _ string, _ ...v1.DeleteOptions) (*types.DeletionResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Delete is not supported by the fake client"} -} - -// Compile-time check that fakeProfileClient implements v1.ProfileInterface. -var _ v1.ProfileInterface = (*fakeProfileClient)(nil) diff --git a/sdk/go/openshell/v1/fake/profile_test.go b/sdk/go/openshell/v1/fake/profile_test.go deleted file mode 100644 index b22ed9709d..0000000000 --- a/sdk/go/openshell/v1/fake/profile_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- T027: fakeProfileClient stub tests --- - -func TestFakeProfile_List_ReturnsUnimplemented(t *testing.T) { - c := newFakeProfileClient(func() bool { return false }) - _, err := c.ListAll(context.Background(), "default") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeProfile_Get_ReturnsUnimplemented(t *testing.T) { - c := newFakeProfileClient(func() bool { return false }) - _, err := c.Get(context.Background(), "default", "profile-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeProfile_Import_ReturnsUnimplemented(t *testing.T) { - c := newFakeProfileClient(func() bool { return false }) - _, err := c.Import(context.Background(), "default", []types.ProfileImportItem{{Source: "test"}}) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeProfile_Update_ReturnsUnimplemented(t *testing.T) { - c := newFakeProfileClient(func() bool { return false }) - _, err := c.Update(context.Background(), "default", "profile-1", 1, types.ProfileImportItem{Source: "test"}) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeProfile_Lint_ReturnsUnimplemented(t *testing.T) { - c := newFakeProfileClient(func() bool { return false }) - _, err := c.Lint(context.Background(), "default", []types.ProfileImportItem{{Source: "test"}}) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeProfile_Delete_ReturnsUnimplemented(t *testing.T) { - c := newFakeProfileClient(func() bool { return false }) - _, err := c.Delete(context.Background(), "default", "profile-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeProfile_List_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeProfileClient(func() bool { return true }) - _, err := c.ListAll(context.Background(), "default") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeProfile_Get_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeProfileClient(func() bool { return true }) - _, err := c.Get(context.Background(), "default", "profile-1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeProfile_Import_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeProfileClient(func() bool { return true }) - _, err := c.Import(context.Background(), "default", []types.ProfileImportItem{{Source: "test"}}) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeProfile_Update_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeProfileClient(func() bool { return true }) - _, err := c.Update(context.Background(), "default", "profile-1", 1, types.ProfileImportItem{Source: "test"}) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeProfile_Lint_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeProfileClient(func() bool { return true }) - _, err := c.Lint(context.Background(), "default", []types.ProfileImportItem{{Source: "test"}}) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeProfile_Delete_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeProfileClient(func() bool { return true }) - _, err := c.Delete(context.Background(), "default", "profile-1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} diff --git a/sdk/go/openshell/v1/fake/provider.go b/sdk/go/openshell/v1/fake/provider.go deleted file mode 100644 index 85512318d2..0000000000 --- a/sdk/go/openshell/v1/fake/provider.go +++ /dev/null @@ -1,190 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "time" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// providerName extracts the name from a Provider pointer for use as the -// objectStore key function. -func providerName(p *types.Provider) string { - return p.Name -} - -// copyProvider returns a deep copy of a Provider pointer. All maps are -// duplicated to prevent aliasing. -func copyProvider(p *types.Provider) *types.Provider { - if p == nil { - return nil - } - cp := *p - cp.Labels = copyStringMap(p.Labels) - cp.Annotations = copyStringMap(p.Annotations) - if p.DeletionTimestamp != nil { - t := *p.DeletionTimestamp - cp.DeletionTimestamp = &t - } - cp.Spec = copyProviderSpec(p.Spec) - return &cp -} - -func copyProviderSpec(s types.ProviderSpec) types.ProviderSpec { - s.Credentials = copyStringMap(s.Credentials) - s.Config = copyStringMap(s.Config) - s.CredentialExpiresAt = copyTimeMap(s.CredentialExpiresAt) - return s -} - -// copyTimeMap returns a shallow copy of a string-to-time.Time map. -func copyTimeMap(m map[string]time.Time) map[string]time.Time { - if m == nil { - return nil - } - cp := make(map[string]time.Time, len(m)) - for k, v := range m { - cp[k] = v - } - return cp -} - -// fakeProviderClient implements v1.ProviderInterface backed by an in-memory -// objectStore. -type fakeProviderClient struct { - store *objectStore[*types.Provider] - closedFunc func() bool - profiles *fakeProfileClient - refresh *fakeRefreshClient -} - -// newFakeProviderClient creates a new fakeProviderClient. -func newFakeProviderClient( - store *objectStore[*types.Provider], - closedFunc func() bool, -) *fakeProviderClient { - return &fakeProviderClient{ - store: store, - closedFunc: closedFunc, - profiles: newFakeProfileClient(closedFunc), - refresh: newFakeRefreshClient(closedFunc), - } -} - -// Profiles returns a sub-client for provider profile operations. -func (c *fakeProviderClient) Profiles() v1.ProfileInterface { - return c.profiles -} - -// Refresh returns a sub-client for credential refresh operations. -func (c *fakeProviderClient) Refresh() v1.RefreshInterface { - return c.refresh -} - -// Create adds a new provider. CreatedAt and ResourceVersion are set -// automatically. -func (c *fakeProviderClient) Create(_ context.Context, workspace string, provider *types.Provider) (*types.Provider, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if provider == nil { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "provider must not be nil"} - } - - p := copyProvider(provider) - p.Workspace = workspace - p.CreatedAt = time.Now() - p.ResourceVersion = 1 - - return c.store.Create(workspace, p) -} - -// Get retrieves a provider by name. -func (c *fakeProviderClient) Get(_ context.Context, workspace, name string) (*types.Provider, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return c.store.Get(workspace, name) -} - -// List returns a lazy pager over providers. Filtering is not implemented. -func (c *fakeProviderClient) List(workspace string, opts ...v1.ListOptions) (*v1.Pager[*types.Provider], error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - var options v1.ListOptions - if len(opts) > 0 { - options = opts[0] - } - items := c.store.List(workspace) - if len(opts) > 0 && opts[0].AllWorkspaces { - items = c.store.ListAll() - } - return newSlicePager(items, options.PageSize, options.PageToken) -} - -func (c *fakeProviderClient) ListAll(ctx context.Context, workspace string, opts ...v1.ListOptions) ([]*types.Provider, error) { - pager, err := c.List(workspace, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -// Update replaces an existing provider's data. ResourceVersion is -// incremented automatically. -func (c *fakeProviderClient) Update(_ context.Context, workspace string, provider *types.Provider) (*types.Provider, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if provider == nil { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "provider must not be nil"} - } - - existing, err := c.store.Get(workspace, provider.Name) - if err != nil { - return nil, err - } - - p := copyProvider(provider) - p.Workspace = workspace - p.CreatedAt = existing.CreatedAt - p.ResourceVersion = existing.ResourceVersion + 1 - - return c.store.Update(workspace, p) -} - -// Delete removes a provider by name. The operation is idempotent. -func (c *fakeProviderClient) Delete(_ context.Context, workspace, name string, opts ...v1.DeleteOptions) (*types.DeletionResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - _, existed := c.store.DeleteAndGet(workspace, name) - return deletionResult(existed, "", opts) -} - -// Ensure creates a provider if it does not exist, or updates it if it does. -func (c *fakeProviderClient) Ensure(ctx context.Context, workspace string, provider *types.Provider) (*types.Provider, error) { - if provider == nil { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "provider must not be nil"} - } - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - existing, err := c.store.Get(workspace, provider.Name) - if err != nil { - if types.IsNotFound(err) { - return c.Create(ctx, workspace, provider) - } - return nil, err - } - updated := copyProvider(provider) - updated.ID = existing.ID - updated.ResourceVersion = existing.ResourceVersion - return c.Update(ctx, workspace, updated) -} diff --git a/sdk/go/openshell/v1/fake/provider_test.go b/sdk/go/openshell/v1/fake/provider_test.go deleted file mode 100644 index c729d063ac..0000000000 --- a/sdk/go/openshell/v1/fake/provider_test.go +++ /dev/null @@ -1,276 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "fmt" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// helper to build a minimal fake provider client for testing. -func newTestProviderClient() *fakeProviderClient { - store := newobjectStore(providerName, copyProvider) - return newFakeProviderClient(store, func() bool { return false }) -} - -// --- T015: Provider CRUD tests --- - -func TestProvider_Create(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - p := &types.Provider{ - Name: "openai", - Type: "openai", - Spec: types.ProviderSpec{ - Credentials: map[string]string{"api_key": "sk-test"}, - Config: map[string]string{"model": "gpt-4"}, - }, - } - - result, err := pc.Create(ctx, "default", p) - require.NoError(t, err) - assert.Equal(t, "openai", result.Name) - assert.Equal(t, "openai", result.Type) - assert.Equal(t, "sk-test", result.Spec.Credentials["api_key"]) - assert.NotZero(t, result.CreatedAt) - assert.Equal(t, uint64(1), result.ResourceVersion) -} - -func TestProvider_Create_AlreadyExists(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - p := &types.Provider{Name: "openai", Type: "openai"} - _, err := pc.Create(ctx, "default", p) - require.NoError(t, err) - - _, err = pc.Create(ctx, "default", p) - require.Error(t, err) - assert.True(t, types.IsAlreadyExists(err)) -} - -func TestProvider_Get(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - _, _ = pc.Create(ctx, "default", &types.Provider{Name: "openai", Type: "openai"}) - - got, err := pc.Get(ctx, "default", "openai") - require.NoError(t, err) - assert.Equal(t, "openai", got.Name) -} - -func TestProvider_Get_NotFound(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - _, err := pc.Get(ctx, "default", "nonexistent") - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestProvider_List_Empty(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - list, err := pc.ListAll(ctx, "default") - require.NoError(t, err) - assert.Empty(t, list) -} - -func TestProvider_List(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - _, _ = pc.Create(ctx, "default", &types.Provider{Name: "openai", Type: "openai"}) - _, _ = pc.Create(ctx, "default", &types.Provider{Name: "anthropic", Type: "anthropic"}) - - list, err := pc.ListAll(ctx, "default") - require.NoError(t, err) - assert.Len(t, list, 2) -} - -func TestProvider_Update(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - _, _ = pc.Create(ctx, "default", &types.Provider{ - Name: "openai", - Type: "openai", - Spec: types.ProviderSpec{Config: map[string]string{"model": "gpt-3.5"}}, - }) - - updated, err := pc.Update(ctx, "default", &types.Provider{ - Name: "openai", - Type: "openai", - Spec: types.ProviderSpec{Config: map[string]string{"model": "gpt-4"}}, - }) - require.NoError(t, err) - assert.Equal(t, "gpt-4", updated.Spec.Config["model"]) - assert.Equal(t, uint64(2), updated.ResourceVersion) -} - -func TestProvider_Update_NotFound(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - _, err := pc.Update(ctx, "default", &types.Provider{Name: "nonexistent"}) - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestProvider_Delete(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - _, _ = pc.Create(ctx, "default", &types.Provider{Name: "openai"}) - - _, err := pc.Delete(ctx, "default", "openai") - require.NoError(t, err) - - _, err = pc.Get(ctx, "default", "openai") - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestProvider_Delete_Idempotent(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - _, err := pc.Delete(ctx, "default", "nonexistent", types.DeleteOptions{AllowMissing: true}) - require.NoError(t, err) -} - -func TestProvider_Ensure_CreatesIfMissing(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - p := &types.Provider{ - Name: "openai", - Type: "openai", - Spec: types.ProviderSpec{Config: map[string]string{"model": "gpt-4"}}, - } - - result, err := pc.Ensure(ctx, "default", p) - require.NoError(t, err) - assert.Equal(t, "openai", result.Name) - assert.Equal(t, "gpt-4", result.Spec.Config["model"]) - - // Verify it was stored - got, err := pc.Get(ctx, "default", "openai") - require.NoError(t, err) - assert.Equal(t, "gpt-4", got.Spec.Config["model"]) -} - -func TestProvider_Ensure_UpdatesIfExists(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - _, _ = pc.Create(ctx, "default", &types.Provider{ - Name: "openai", - Type: "openai", - Spec: types.ProviderSpec{Config: map[string]string{"model": "gpt-3.5"}}, - }) - - result, err := pc.Ensure(ctx, "default", &types.Provider{ - Name: "openai", - Type: "openai", - Spec: types.ProviderSpec{Config: map[string]string{"model": "gpt-4"}}, - }) - require.NoError(t, err) - assert.Equal(t, "gpt-4", result.Spec.Config["model"]) -} - -func TestProvider_DeepCopy_OnCreate(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - spec := types.ProviderSpec{ - Credentials: map[string]string{"key": "secret"}, - Config: map[string]string{"model": "gpt-4"}, - CredentialExpiresAt: map[string]time.Time{"key": time.Now()}, - } - p := &types.Provider{ - Name: "openai", - Labels: map[string]string{"env": "test"}, - Spec: spec, - } - - result, err := pc.Create(ctx, "default", p) - require.NoError(t, err) - - // Mutate inputs - p.Labels["env"] = "mutated" - p.Spec.Credentials["key"] = "mutated" - p.Spec.Config["model"] = "mutated" - - got, err := pc.Get(ctx, "default", "openai") - require.NoError(t, err) - assert.Equal(t, "test", got.Labels["env"]) - assert.Equal(t, "secret", got.Spec.Credentials["key"]) - assert.Equal(t, "gpt-4", got.Spec.Config["model"]) - - // Mutate returned object - result.Labels["env"] = "mutated-return" - got2, err := pc.Get(ctx, "default", "openai") - require.NoError(t, err) - assert.Equal(t, "test", got2.Labels["env"]) -} - -func TestProvider_DeepCopy_OnGet(t *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - _, _ = pc.Create(ctx, "default", &types.Provider{ - Name: "openai", - Spec: types.ProviderSpec{Config: map[string]string{"model": "gpt-4"}}, - }) - - got, err := pc.Get(ctx, "default", "openai") - require.NoError(t, err) - - got.Spec.Config["model"] = "mutated" - - got2, err := pc.Get(ctx, "default", "openai") - require.NoError(t, err) - assert.Equal(t, "gpt-4", got2.Spec.Config["model"]) -} - -// --- T020: Concurrent provider access tests --- - -func TestProvider_ConcurrentCreateGetListDeleteEnsure(_ *testing.T) { - pc := newTestProviderClient() - ctx := context.Background() - - const goroutines = 10 - const opsPerGoroutine = 20 - - var wg sync.WaitGroup - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - for j := 0; j < opsPerGoroutine; j++ { - name := fmt.Sprintf("prov-%d-%d", id, j) - p := &types.Provider{Name: name, Type: "test"} - _, _ = pc.Create(ctx, "default", p) - _, _ = pc.Get(ctx, "default", name) - _, _ = pc.ListAll(ctx, "default") - _, _ = pc.Update(ctx, "default", &types.Provider{Name: name, Type: "updated"}) - _, _ = pc.Ensure(ctx, "default", &types.Provider{Name: name, Type: "ensured"}) - _, _ = pc.Delete(ctx, "default", name) - } - }(i) - } - wg.Wait() -} diff --git a/sdk/go/openshell/v1/fake/refresh.go b/sdk/go/openshell/v1/fake/refresh.go deleted file mode 100644 index d7ac38d105..0000000000 --- a/sdk/go/openshell/v1/fake/refresh.go +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// fakeRefreshClient implements v1.RefreshInterface. All methods return -// Unimplemented because credential refresh requires a real server. -type fakeRefreshClient struct { - closedFunc func() bool -} - -// newFakeRefreshClient creates a new fakeRefreshClient. -func newFakeRefreshClient(closedFunc func() bool) *fakeRefreshClient { - return &fakeRefreshClient{closedFunc: closedFunc} -} - -// GetStatus returns Unimplemented. -func (c *fakeRefreshClient) GetStatus(_ context.Context, _, _, _ string) ([]*types.RefreshStatus, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "GetStatus is not supported by the fake client"} -} - -// Configure returns Unimplemented. -func (c *fakeRefreshClient) Configure(_ context.Context, _ string, _ *types.RefreshConfig) (*types.RefreshStatus, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Configure is not supported by the fake client"} -} - -// Rotate returns Unimplemented. -func (c *fakeRefreshClient) Rotate(_ context.Context, _, _, _ string) (*types.RefreshStatus, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Rotate is not supported by the fake client"} -} - -// Delete returns Unimplemented. -func (c *fakeRefreshClient) Delete(_ context.Context, _, _, _ string, _ ...v1.DeleteOptions) (*types.DeletionResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Delete is not supported by the fake client"} -} - -// Compile-time check that fakeRefreshClient implements v1.RefreshInterface. -var _ v1.RefreshInterface = (*fakeRefreshClient)(nil) diff --git a/sdk/go/openshell/v1/fake/refresh_test.go b/sdk/go/openshell/v1/fake/refresh_test.go deleted file mode 100644 index 4af951dcd3..0000000000 --- a/sdk/go/openshell/v1/fake/refresh_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- T028: fakeRefreshClient stub tests --- - -func TestFakeRefresh_GetStatus_ReturnsUnimplemented(t *testing.T) { - c := newFakeRefreshClient(func() bool { return false }) - _, err := c.GetStatus(context.Background(), "default", "provider-1", "cred-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeRefresh_Configure_ReturnsUnimplemented(t *testing.T) { - c := newFakeRefreshClient(func() bool { return false }) - _, err := c.Configure(context.Background(), "default", &types.RefreshConfig{ - Provider: "provider-1", - CredentialKey: "cred-1", - }) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeRefresh_Rotate_ReturnsUnimplemented(t *testing.T) { - c := newFakeRefreshClient(func() bool { return false }) - _, err := c.Rotate(context.Background(), "default", "provider-1", "cred-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeRefresh_Delete_ReturnsUnimplemented(t *testing.T) { - c := newFakeRefreshClient(func() bool { return false }) - _, err := c.Delete(context.Background(), "default", "provider-1", "cred-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeRefresh_GetStatus_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeRefreshClient(func() bool { return true }) - _, err := c.GetStatus(context.Background(), "default", "provider-1", "cred-1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeRefresh_Configure_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeRefreshClient(func() bool { return true }) - _, err := c.Configure(context.Background(), "default", &types.RefreshConfig{ - Provider: "provider-1", - CredentialKey: "cred-1", - }) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeRefresh_Rotate_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeRefreshClient(func() bool { return true }) - _, err := c.Rotate(context.Background(), "default", "provider-1", "cred-1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeRefresh_Delete_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeRefreshClient(func() bool { return true }) - _, err := c.Delete(context.Background(), "default", "provider-1", "cred-1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} diff --git a/sdk/go/openshell/v1/fake/sandbox.go b/sdk/go/openshell/v1/fake/sandbox.go deleted file mode 100644 index 221e1f7524..0000000000 --- a/sdk/go/openshell/v1/fake/sandbox.go +++ /dev/null @@ -1,932 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "fmt" - "slices" - "sync" - "time" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// sandboxName extracts the name from a Sandbox pointer for use as the -// objectStore key function. -func sandboxName(sb *types.Sandbox) string { - return sb.Name -} - -// copySandbox returns a deep copy of a Sandbox pointer. All maps, slices, -// and nested pointer fields are duplicated to prevent aliasing. -func copySandbox(sb *types.Sandbox) *types.Sandbox { - if sb == nil { - return nil - } - cp := *sb - cp.Labels = copyStringMap(sb.Labels) - cp.Annotations = copyStringMap(sb.Annotations) - if sb.DeletionTimestamp != nil { - t := *sb.DeletionTimestamp - cp.DeletionTimestamp = &t - } - if sb.CreatedFromWorkloadTemplate != nil { - provenance := *sb.CreatedFromWorkloadTemplate - cp.CreatedFromWorkloadTemplate = &provenance - } - cp.Spec = copySandboxSpec(sb.Spec) - cp.Status = copySandboxStatus(sb.Status) - return &cp -} - -func copySandboxSpec(s types.SandboxSpec) types.SandboxSpec { - s.Environment = copyStringMap(s.Environment) - s.Providers = copyStringSlice(s.Providers) - s.Command = copyStringSlice(s.Command) - if s.Template != nil { - t := copySandboxTemplate(*s.Template) - s.Template = &t - } - if s.GPUCount != nil { - v := *s.GPUCount - s.GPUCount = &v - } - s.Policy = copySandboxPolicy(s.Policy) - return s -} - -// copySandboxPolicy returns a deep copy of a SandboxPolicy pointer. -// All sub-policies, slices, and map entries are duplicated. -func copySandboxPolicy(p *types.SandboxPolicy) *types.SandboxPolicy { - if p == nil { - return nil - } - cp := *p - if p.Filesystem != nil { - fs := *p.Filesystem - fs.ReadOnly = copyStringSlice(p.Filesystem.ReadOnly) - fs.ReadWrite = copyStringSlice(p.Filesystem.ReadWrite) - cp.Filesystem = &fs - } - if p.Landlock != nil { - ll := *p.Landlock - cp.Landlock = &ll - } - if p.Process != nil { - pr := *p.Process - cp.Process = &pr - } - if p.NetworkPolicies != nil { - np := make(map[string]types.NetworkPolicyRule, len(p.NetworkPolicies)) - for k, rule := range p.NetworkPolicies { - r := rule - if rule.Endpoints != nil { - eps := make([]types.PolicyNetworkEndpoint, len(rule.Endpoints)) - for i, ep := range rule.Endpoints { - eps[i] = copyPolicyNetworkEndpoint(ep) - } - r.Endpoints = eps - } - if rule.Binaries != nil { - bins := make([]types.PolicyNetworkBinary, len(rule.Binaries)) - copy(bins, rule.Binaries) - r.Binaries = bins - } - np[k] = r - } - cp.NetworkPolicies = np - } - if p.NetworkMiddlewares != nil { - nm := make(map[string]types.NetworkMiddlewareConfig, len(p.NetworkMiddlewares)) - for k, mw := range p.NetworkMiddlewares { - mw.Config = copyAnyMap(mw.Config) - if mw.Endpoints != nil { - ep := *mw.Endpoints - ep.Include = copyStringSlice(mw.Endpoints.Include) - ep.Exclude = copyStringSlice(mw.Endpoints.Exclude) - mw.Endpoints = &ep - } - nm[k] = mw - } - cp.NetworkMiddlewares = nm - } - return &cp -} - -func copyPolicyNetworkEndpoint(ep types.PolicyNetworkEndpoint) types.PolicyNetworkEndpoint { - if ep.Ports != nil { - ports := make([]uint32, len(ep.Ports)) - copy(ports, ep.Ports) - ep.Ports = ports - } - if ep.Rules != nil { - rules := make([]types.L7Rule, len(ep.Rules)) - for i, r := range ep.Rules { - rules[i] = r - if r.Allow != nil { - a := *r.Allow - a.Query = copyL7QueryMap(r.Allow.Query) - a.Fields = copyStringSlice(r.Allow.Fields) - a.Params = copyL7QueryMap(r.Allow.Params) - rules[i].Allow = &a - } - } - ep.Rules = rules - } - ep.AllowedIPs = copyStringSlice(ep.AllowedIPs) - if ep.DenyRules != nil { - drs := make([]types.L7DenyRule, len(ep.DenyRules)) - for i, dr := range ep.DenyRules { - dr.Query = copyL7QueryMap(dr.Query) - dr.Fields = copyStringSlice(dr.Fields) - dr.Params = copyL7QueryMap(dr.Params) - drs[i] = dr - } - ep.DenyRules = drs - } - if ep.GraphqlPersistedQueries != nil { - gq := make(map[string]types.GraphqlOperation, len(ep.GraphqlPersistedQueries)) - for k, v := range ep.GraphqlPersistedQueries { - v.Fields = copyStringSlice(v.Fields) - gq[k] = v - } - ep.GraphqlPersistedQueries = gq - } - if ep.CredentialBinding != nil { - cb := *ep.CredentialBinding - ep.CredentialBinding = &cb - } - if ep.Mcp != nil { - mcp := *ep.Mcp - mcp.StrictToolNames = copyBoolPtr(ep.Mcp.StrictToolNames) - mcp.AllowAllKnownMcpMethods = copyBoolPtr(ep.Mcp.AllowAllKnownMcpMethods) - ep.Mcp = &mcp - } - return ep -} - -func copyBoolPtr(p *bool) *bool { - if p == nil { - return nil - } - v := *p - return &v -} - -func copyL7QueryMap(m map[string]types.L7QueryMatcher) map[string]types.L7QueryMatcher { - if m == nil { - return nil - } - cp := make(map[string]types.L7QueryMatcher, len(m)) - for k, v := range m { - v.Any = copyStringSlice(v.Any) - cp[k] = v - } - return cp -} - -func copySandboxTemplate(t types.SandboxTemplate) types.SandboxTemplate { - t.Labels = copyStringMap(t.Labels) - t.Annotations = copyStringMap(t.Annotations) - t.Environment = copyStringMap(t.Environment) - if t.UserNamespaces != nil { - v := *t.UserNamespaces - t.UserNamespaces = &v - } - t.Resources = copyAnyMap(t.Resources) - t.DriverConfig = copyAnyMap(t.DriverConfig) - return t -} - -func copyAnyMap(m map[string]any) map[string]any { - if m == nil { - return nil - } - cp := make(map[string]any, len(m)) - for k, v := range m { - cp[k] = copyAnyValue(v) - } - return cp -} - -func copyAnyValue(v any) any { - switch val := v.(type) { - case map[string]any: - return copyAnyMap(val) - case []any: - s := make([]any, len(val)) - for i, elem := range val { - s[i] = copyAnyValue(elem) - } - return s - default: - return v - } -} - -func copySandboxStatus(s types.SandboxStatus) types.SandboxStatus { - if s.Conditions != nil { - conds := make([]types.SandboxCondition, len(s.Conditions)) - copy(conds, s.Conditions) - s.Conditions = conds - } - // Callers may mutate endpoint snapshots without changing the fake's storage. - s.EndpointStatuses = slices.Clone(s.EndpointStatuses) - for i := range s.EndpointStatuses { - s.EndpointStatuses[i].Ports = slices.Clone(s.EndpointStatuses[i].Ports) - s.EndpointStatuses[i].LastFailure = copyEndpointFailure(s.EndpointStatuses[i].LastFailure) - } - if s.LastTokenGrantFailure != nil { - observation := *s.LastTokenGrantFailure - observation.Failure = copyEndpointFailure(observation.Failure) - s.LastTokenGrantFailure = &observation - } - return s -} - -func copyEndpointFailure(f *types.EndpointFailure) *types.EndpointFailure { - if f == nil { - return nil - } - cp := *f - if f.UpstreamHTTPStatus != nil { - status := *f.UpstreamHTTPStatus - cp.UpstreamHTTPStatus = &status - } - return &cp -} - -// copyStringMap returns a shallow copy of a string-to-string map. -func copyStringMap(m map[string]string) map[string]string { - if m == nil { - return nil - } - cp := make(map[string]string, len(m)) - for k, v := range m { - cp[k] = v - } - return cp -} - -// copyStringSlice returns a copy of a string slice. -func copyStringSlice(s []string) []string { - if s == nil { - return nil - } - cp := make([]string, len(s)) - copy(cp, s) - return cp -} - -// fakeSandboxClient implements v1.SandboxInterface backed by an in-memory -// objectStore and watchBroadcaster. -type fakeSandboxClient struct { - store *objectStore[*types.Sandbox] - templateStore *objectStore[*types.SandboxWorkloadTemplate] - broadcaster *watchBroadcaster[*types.Sandbox] - closedFunc func() bool -} - -var _ v1.SandboxInterface = (*fakeSandboxClient)(nil) -var _ v1.SandboxTemplateCreateInterface = (*fakeSandboxClient)(nil) -var _ v1.SandboxSuspendResumeInterface = (*fakeSandboxClient)(nil) - -// newFakeSandboxClient creates a new fakeSandboxClient. -func newFakeSandboxClient( - store *objectStore[*types.Sandbox], - templateStore *objectStore[*types.SandboxWorkloadTemplate], - broadcaster *watchBroadcaster[*types.Sandbox], - closedFunc func() bool, -) *fakeSandboxClient { - return &fakeSandboxClient{ - store: store, - templateStore: templateStore, - broadcaster: broadcaster, - closedFunc: closedFunc, - } -} - -// Create creates a new sandbox with Provisioning phase. -func (c *fakeSandboxClient) Create(_ context.Context, workspace, name string, spec *types.SandboxSpec, opts ...types.CreateOption) (*types.Sandbox, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - if spec == nil { - spec = &types.SandboxSpec{} - } - - cfg := types.ApplyCreateOptions(opts) - - sb := &types.Sandbox{ - Name: name, - Workspace: workspace, - CreatedAt: time.Now(), - Labels: copyStringMap(cfg.Labels()), - Annotations: copyStringMap(cfg.Annotations()), - ResourceVersion: 1, - Spec: copySandboxSpec(*spec), - Status: types.SandboxStatus{ - Phase: types.SandboxProvisioning, - }, - } - - result, err := c.store.Create(workspace, sb) - if err != nil { - return nil, err - } - - c.broadcaster.Broadcast(types.Event[*types.Sandbox]{ - Type: types.EventAdded, - Object: copySandbox(result), - }, name) - - return result, nil -} - -// CreateFromTemplate creates a new sandbox from a named template with Provisioning phase. -func (c *fakeSandboxClient) CreateFromTemplate(_ context.Context, workspace, name, templateName string, spec *types.SandboxSpec, opts ...types.CreateOption) (*types.Sandbox, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if templateName == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "template name is required"} - } - if err := validateTemplateCreateSpec(spec); err != nil { - return nil, err - } - template, err := c.templateStore.Get(workspace, templateName) - if err != nil { - return nil, err - } - if spec == nil { - spec = &types.SandboxSpec{} - } - - cfg := types.ApplyCreateOptions(opts) - - resolvedSpec := sandboxSpecFromWorkloadTemplate(template) - resolvedSpec.Providers = copyStringSlice(spec.Providers) - resolvedSpec.Policy = copySandboxPolicy(spec.Policy) - resolvedSpec.Command = copyStringSlice(spec.Command) - resolvedSpec.TTY = spec.TTY - - sb := &types.Sandbox{ - Name: name, - Workspace: workspace, - CreatedAt: time.Now(), - Labels: copyStringMap(cfg.Labels()), - Annotations: copyStringMap(cfg.Annotations()), - ResourceVersion: 1, - Spec: resolvedSpec, - CreatedFromWorkloadTemplate: &types.SandboxWorkloadTemplateProvenance{ - Name: template.Name, - ResourceVersion: fmt.Sprint(template.ResourceVersion), - }, - Status: types.SandboxStatus{ - Phase: types.SandboxProvisioning, - }, - } - - result, err := c.store.Create(workspace, sb) - if err != nil { - return nil, err - } - - c.broadcaster.Broadcast(types.Event[*types.Sandbox]{ - Type: types.EventAdded, - Object: copySandbox(result), - }, name) - - return result, nil -} - -func validateTemplateCreateSpec(spec *types.SandboxSpec) error { - if spec == nil { - return nil - } - if spec.LogLevel != "" || len(spec.Environment) > 0 || spec.Template != nil || spec.GPU || spec.GPUCount != nil { - return &types.StatusError{Code: types.ErrorInvalidArgument, Message: "template creates only allow policy, providers, command, and tty in spec"} - } - return nil -} - -func sandboxSpecFromWorkloadTemplate(template *types.SandboxWorkloadTemplate) types.SandboxSpec { - var spec types.SandboxSpec - if template == nil || template.Spec.Workload == nil { - return spec - } - - workload := template.Spec.Workload - spec.Environment = copyStringMap(workload.Environment) - spec.Template = &types.SandboxTemplate{ - Image: workload.Image, - Resources: sandboxTemplateResources(workload.Resources), - DriverConfig: copyAnyMap(template.Spec.DriverConfig), - } - if workload.Resources != nil && workload.Resources.GPU != nil { - spec.GPU = true - if workload.Resources.GPU.Count != nil { - count := *workload.Resources.GPU.Count - spec.GPUCount = &count - } - } - return spec -} - -func sandboxTemplateResources(resources *types.SandboxResources) map[string]any { - if resources == nil { - return nil - } - limits := make(map[string]any) - if resources.CPU != "" { - limits["cpu"] = resources.CPU - } - if resources.Memory != "" { - limits["memory"] = resources.Memory - } - if len(limits) == 0 { - return nil - } - return map[string]any{"limits": limits} -} - -// Get retrieves a sandbox by name. -func (c *fakeSandboxClient) Get(_ context.Context, workspace, name string) (*types.Sandbox, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return c.store.Get(workspace, name) -} - -// List returns a lazy pager over sandboxes. Filtering is not implemented. -func (c *fakeSandboxClient) List(workspace string, opts ...v1.ListOptions) (*v1.Pager[*types.Sandbox], error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - var options v1.ListOptions - if len(opts) > 0 { - options = opts[0] - } - items := c.store.List(workspace) - if len(opts) > 0 && opts[0].AllWorkspaces { - items = c.store.ListAll() - } - return newSlicePager(items, options.PageSize, options.PageToken) -} - -func (c *fakeSandboxClient) ListAll(ctx context.Context, workspace string, opts ...v1.ListOptions) ([]*types.Sandbox, error) { - pager, err := c.List(workspace, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -// Stop transitions a sandbox to the Stopped phase. -func (c *fakeSandboxClient) Stop(_ context.Context, workspace, name string) (*types.Sandbox, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - sb, err := c.store.Get(workspace, name) - if err != nil { - return nil, err - } - - sb.Status.Phase = types.SandboxStopped - sb.ResourceVersion++ - - updated, err := c.store.Update(workspace, sb) - if err != nil { - return nil, err - } - - c.broadcaster.Broadcast(types.Event[*types.Sandbox]{ - Type: types.EventModified, - Object: copySandbox(updated), - }, name) - - return updated, nil -} - -// Start transitions a stopped sandbox back to the Ready phase. -func (c *fakeSandboxClient) Start(_ context.Context, workspace, name string) (*types.Sandbox, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - sb, err := c.store.Get(workspace, name) - if err != nil { - return nil, err - } - - sb.Status.Phase = types.SandboxReady - sb.ResourceVersion++ - - updated, err := c.store.Update(workspace, sb) - if err != nil { - return nil, err - } - - c.broadcaster.Broadcast(types.Event[*types.Sandbox]{ - Type: types.EventModified, - Object: copySandbox(updated), - }, name) - - return updated, nil -} - -// Suspend transitions a Ready sandbox to the Suspended phase. -func (c *fakeSandboxClient) Suspend(_ context.Context, workspace, name string) (*types.Sandbox, error) { - return c.transitionFrom(workspace, name, types.SandboxSuspended, types.SandboxReady, types.SandboxSuspending, types.SandboxSuspended) -} - -// Resume transitions a Suspended sandbox back to the Ready phase. -func (c *fakeSandboxClient) Resume(_ context.Context, workspace, name string, _ ...v1.ResumeOptions) (*types.Sandbox, error) { - return c.transitionFrom(workspace, name, types.SandboxReady, types.SandboxSuspended) -} - -// WaitSuspended returns the sandbox once it is in the Suspended phase. In the -// fake implementation Suspend completes synchronously, so any other phase is -// reported as an error. -func (c *fakeSandboxClient) WaitSuspended(ctx context.Context, workspace, name string, _ ...v1.WaitOptions) (*types.Sandbox, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if err := ctx.Err(); err != nil { - code := types.ErrorInternal - switch err { - case context.DeadlineExceeded: - code = types.ErrorDeadlineExceeded - case context.Canceled: - code = types.ErrorCancelled - } - return nil, &types.StatusError{Code: code, Message: err.Error(), Cause: err} - } - - sb, err := c.store.Get(workspace, name) - if err != nil { - return nil, err - } - if sb.Status.Phase != types.SandboxSuspended { - return nil, &types.StatusError{Code: types.ErrorInternal, Message: fmt.Sprintf("sandbox %q is %s, not Suspended", name, sb.Status.Phase)} - } - return sb, nil -} - -// transitionFrom moves a sandbox to target when its current phase is one of -// allowed, mirroring the gateway's FAILED_PRECONDITION for other phases. -func (c *fakeSandboxClient) transitionFrom(workspace, name string, target types.SandboxPhase, allowed ...types.SandboxPhase) (*types.Sandbox, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - sb, err := c.store.Get(workspace, name) - if err != nil { - return nil, err - } - if !slices.Contains(allowed, sb.Status.Phase) { - return nil, &types.StatusError{Code: types.ErrorConflict, Message: fmt.Sprintf("sandbox %q cannot move from %s to %s", name, sb.Status.Phase, target)} - } - if sb.Status.Phase == target { - return sb, nil - } - - sb.Status.Phase = target - sb.ResourceVersion++ - - updated, err := c.store.Update(workspace, sb) - if err != nil { - return nil, err - } - - c.broadcaster.Broadcast(types.Event[*types.Sandbox]{ - Type: types.EventModified, - Object: copySandbox(updated), - }, name) - - return updated, nil -} - -// WaitStopped polls until a sandbox reaches the Stopped phase. -func (c *fakeSandboxClient) WaitStopped(ctx context.Context, workspace, name string, _ ...v1.WaitOptions) (*types.Sandbox, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - select { - case <-ctx.Done(): - err := ctx.Err() - switch err { - case context.DeadlineExceeded: - return nil, &types.StatusError{Code: types.ErrorDeadlineExceeded, Message: err.Error(), Cause: err} - case context.Canceled: - return nil, &types.StatusError{Code: types.ErrorCancelled, Message: err.Error(), Cause: err} - default: - return nil, &types.StatusError{Code: types.ErrorInternal, Message: err.Error(), Cause: err} - } - default: - } - - sb, err := c.store.Get(workspace, name) - if err != nil { - return nil, err - } - - if sb.Status.Phase == types.SandboxStopped { - return sb, nil - } - - sb.Status.Phase = types.SandboxStopped - sb.ResourceVersion++ - - updated, err := c.store.Update(workspace, sb) - if err != nil { - return nil, err - } - - c.broadcaster.Broadcast(types.Event[*types.Sandbox]{ - Type: types.EventModified, - Object: copySandbox(updated), - }, name) - - return updated, nil -} - -// Delete removes a sandbox by name. The operation is idempotent. -func (c *fakeSandboxClient) Delete(_ context.Context, workspace, name string, opts ...v1.DeleteOptions) (*types.DeletionResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - deleted, existed := c.store.DeleteAndGet(workspace, name) - if !existed { - return deletionResult(false, "", opts) - } - - c.broadcaster.Broadcast(types.Event[*types.Sandbox]{ - Type: types.EventDeleted, - Object: deleted, - }, name) - - return deletionResult(true, deleted.ID, opts) -} - -// WaitReady transitions a sandbox to the Ready phase. In the fake -// implementation this happens synchronously — context cancellation is -// checked first to support timeout testing. -func (c *fakeSandboxClient) WaitReady(ctx context.Context, workspace, name string, _ ...v1.WaitOptions) (*types.Sandbox, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - select { - case <-ctx.Done(): - err := ctx.Err() - switch err { - case context.DeadlineExceeded: - return nil, &types.StatusError{Code: types.ErrorDeadlineExceeded, Message: err.Error(), Cause: err} - case context.Canceled: - return nil, &types.StatusError{Code: types.ErrorCancelled, Message: err.Error(), Cause: err} - default: - return nil, &types.StatusError{Code: types.ErrorInternal, Message: err.Error(), Cause: err} - } - default: - } - - sb, err := c.store.Get(workspace, name) - if err != nil { - return nil, err - } - - // If already ready, return immediately - if sb.Status.Phase == types.SandboxReady { - return sb, nil - } - - sb.Status.Phase = types.SandboxReady - sb.ResourceVersion++ - - updated, err := c.store.Update(workspace, sb) - if err != nil { - return nil, fmt.Errorf("updating sandbox phase: %w", err) - } - - c.broadcaster.Broadcast(types.Event[*types.Sandbox]{ - Type: types.EventModified, - Object: copySandbox(updated), - }, name) - - return updated, nil -} - -// Watch registers a watcher for sandbox events. If name is non-empty, only -// events for that sandbox are delivered. When StopOnTerminal is set, the -// watcher auto-closes after delivering a terminal phase event (SandboxReady, -// SandboxCompleted, SandboxStopped, or SandboxError). -func (c *fakeSandboxClient) Watch(ctx context.Context, _, name string, opts ...v1.WatchOptions) (types.WatchInterface[*types.Sandbox], error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - inner := c.broadcaster.Watch(name) - if done := ctx.Done(); done != nil { - go func() { - <-done - inner.Stop() - }() - } - - var stopOnTerminal bool - if len(opts) > 0 { - stopOnTerminal = opts[0].StopOnTerminal - } - - if !stopOnTerminal { - return inner, nil - } - - // Wrap with a filtering watcher that auto-stops after terminal events. - out := make(chan types.Event[*types.Sandbox], watchChannelBuffer) - tw := &terminalWatcher{ - ch: out, - inner: inner, - stopCh: make(chan struct{}), - } - go func() { - defer close(out) - for ev := range inner.ResultChan() { - select { - case out <- ev: - case <-ctx.Done(): - inner.Stop() - return - case <-tw.stopCh: - return - } - if ev.Object != nil && - (ev.Object.Status.Phase == types.SandboxReady || ev.Object.Status.Phase == types.SandboxCompleted || ev.Object.Status.Phase == types.SandboxStopped || ev.Object.Status.Phase == types.SandboxError) { - inner.Stop() - return - } - } - }() - return tw, nil -} - -// terminalWatcher wraps an inner watcher and exposes its own output channel. -type terminalWatcher struct { - ch chan types.Event[*types.Sandbox] - inner types.WatchInterface[*types.Sandbox] - once sync.Once - stopCh chan struct{} -} - -func (w *terminalWatcher) ResultChan() <-chan types.Event[*types.Sandbox] { - return w.ch -} - -func (w *terminalWatcher) Stop() { - w.once.Do(func() { - close(w.stopCh) - w.inner.Stop() - }) -} - -// AttachProvider adds a provider name to the sandbox's Spec.Providers list. -// If the provider is already attached, Attached is false (idempotent). -// The sandbox's ResourceVersion is incremented and a MODIFIED event is -// broadcast. -func (c *fakeSandboxClient) AttachProvider(_ context.Context, workspace, sandboxName, providerName string, _ uint64) (*types.AttachProviderResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - sb, err := c.store.Get(workspace, sandboxName) - if err != nil { - return nil, err - } - - // Check if already attached - for _, p := range sb.Spec.Providers { - if p == providerName { - return &types.AttachProviderResult{ - Sandbox: sb, - Attached: false, - }, nil - } - } - - sb.Spec.Providers = append(sb.Spec.Providers, providerName) - sb.ResourceVersion++ - - updated, err := c.store.Update(workspace, sb) - if err != nil { - return nil, err - } - - c.broadcaster.Broadcast(types.Event[*types.Sandbox]{ - Type: types.EventModified, - Object: copySandbox(updated), - }, sandboxName) - - return &types.AttachProviderResult{ - Sandbox: updated, - Attached: true, - }, nil -} - -// DetachProvider removes a provider name from the sandbox's Spec.Providers -// list. If the provider is not attached, Detached is false (idempotent). -// The sandbox's ResourceVersion is incremented and a MODIFIED event is -// broadcast when a provider is actually removed. -func (c *fakeSandboxClient) DetachProvider(_ context.Context, workspace, sandboxName, providerName string, _ uint64) (*types.DetachProviderResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - sb, err := c.store.Get(workspace, sandboxName) - if err != nil { - return nil, err - } - - // Find and remove the provider - found := false - providers := make([]string, 0, len(sb.Spec.Providers)) - for _, p := range sb.Spec.Providers { - if p == providerName { - found = true - continue - } - providers = append(providers, p) - } - - if !found { - return &types.DetachProviderResult{ - Sandbox: sb, - Detached: false, - }, nil - } - - sb.Spec.Providers = providers - sb.ResourceVersion++ - - updated, err := c.store.Update(workspace, sb) - if err != nil { - return nil, err - } - - c.broadcaster.Broadcast(types.Event[*types.Sandbox]{ - Type: types.EventModified, - Object: copySandbox(updated), - }, sandboxName) - - return &types.DetachProviderResult{ - Sandbox: updated, - Detached: true, - }, nil -} - -// GetLogs returns Unimplemented — fake log retrieval is not yet supported. -func (c *fakeSandboxClient) GetLogs(_ context.Context, _, _ string, _ ...v1.LogOption) (*types.LogResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "GetLogs not implemented in fake client"} -} - -// ListProviders returns a pager over stub Provider objects attached to the -// sandbox. The returned providers contain only the Name field, since the fake -// client does not maintain a full provider registry per sandbox. -func (c *fakeSandboxClient) ListProviders(workspace, sandboxName string, opts ...v1.ListOptions) (*v1.Pager[*types.Provider], error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - - sb, err := c.store.Get(workspace, sandboxName) - if err != nil { - return nil, err - } - - result := make([]*types.Provider, len(sb.Spec.Providers)) - for i, name := range sb.Spec.Providers { - result[i] = &types.Provider{Name: name} - } - var options v1.ListOptions - if len(opts) > 0 { - options = opts[0] - } - return newSlicePager(result, options.PageSize, options.PageToken) -} - -func (c *fakeSandboxClient) ListAllProviders(ctx context.Context, workspace, sandboxName string, opts ...v1.ListOptions) ([]*types.Provider, error) { - pager, err := c.ListProviders(workspace, sandboxName, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} diff --git a/sdk/go/openshell/v1/fake/sandbox_template.go b/sdk/go/openshell/v1/fake/sandbox_template.go deleted file mode 100644 index ba5651d79a..0000000000 --- a/sdk/go/openshell/v1/fake/sandbox_template.go +++ /dev/null @@ -1,292 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "slices" - "strings" - "time" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -func sandboxWorkloadTemplate(template *types.SandboxWorkloadTemplate) string { - return template.Name -} - -func copySandboxWorkloadTemplate(template *types.SandboxWorkloadTemplate) *types.SandboxWorkloadTemplate { - if template == nil { - return nil - } - copied := *template - copied.Labels = copyStringMap(template.Labels) - copied.Annotations = copyStringMap(template.Annotations) - if template.DeletionTimestamp != nil { - t := *template.DeletionTimestamp - copied.DeletionTimestamp = &t - } - copied.Spec = copySandboxWorkloadTemplateSpec(template.Spec) - return &copied -} - -func copySandboxWorkloadTemplateSpec(spec types.SandboxWorkloadTemplateSpec) types.SandboxWorkloadTemplateSpec { - if spec.Workload != nil { - workload := *spec.Workload - workload.Environment = copyStringMap(spec.Workload.Environment) - if spec.Workload.Resources != nil { - resources := *spec.Workload.Resources - if spec.Workload.Resources.GPU != nil { - gpu := *spec.Workload.Resources.GPU - if spec.Workload.Resources.GPU.Count != nil { - count := *spec.Workload.Resources.GPU.Count - gpu.Count = &count - } - resources.GPU = &gpu - } - workload.Resources = &resources - } - spec.Workload = &workload - } - spec.DriverConfig = copyAnyMap(spec.DriverConfig) - if spec.DesiredServiceLevel != nil { - level := *spec.DesiredServiceLevel - if spec.DesiredServiceLevel.Startup != nil { - startup := *spec.DesiredServiceLevel.Startup - level.Startup = &startup - } - spec.DesiredServiceLevel = &level - } - return spec -} - -type fakeSandboxTemplateClient struct { - store *objectStore[*types.SandboxWorkloadTemplate] - closedFunc func() bool -} - -var _ v1.SandboxTemplateInterface = (*fakeSandboxTemplateClient)(nil) - -func newFakeSandboxTemplateClient( - store *objectStore[*types.SandboxWorkloadTemplate], - closedFunc func() bool, -) *fakeSandboxTemplateClient { - return &fakeSandboxTemplateClient{ - store: store, - closedFunc: closedFunc, - } -} - -func (c *fakeSandboxTemplateClient) Create(_ context.Context, workspace string, template *types.SandboxWorkloadTemplate) (*types.SandboxWorkloadTemplate, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if template == nil { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "template must not be nil"} - } - if err := validateSandboxWorkloadTemplate(template); err != nil { - return nil, err - } - - t := copySandboxWorkloadTemplate(template) - t.Workspace = workspace - t.CreatedAt = time.Now() - t.ResourceVersion = 1 - - return c.store.Create(workspace, t) -} - -func (c *fakeSandboxTemplateClient) Get(_ context.Context, workspace, name string) (*types.SandboxWorkloadTemplate, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return c.store.Get(workspace, name) -} - -func (c *fakeSandboxTemplateClient) List(workspace string, opts ...v1.ListOptions) (*v1.Pager[*types.SandboxWorkloadTemplate], error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - var options v1.ListOptions - if len(opts) > 0 { - options = opts[0] - if options.PageSize < 0 { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "page size must not be negative"} - } - } - var templates []*types.SandboxWorkloadTemplate - if options.AllWorkspaces { - templates = c.store.ListAll() - } else { - templates = c.store.List(workspace) - } - slices.SortFunc(templates, compareSandboxWorkloadTemplatesForList) - templates, err := filterSandboxWorkloadTemplatesByLabelSelector(templates, options.LabelSelector) - if err != nil { - return nil, err - } - return newSlicePager(templates, options.PageSize, options.PageToken) -} - -func (c *fakeSandboxTemplateClient) ListAll(ctx context.Context, workspace string, opts ...v1.ListOptions) ([]*types.SandboxWorkloadTemplate, error) { - pager, err := c.List(workspace, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -func (c *fakeSandboxTemplateClient) Delete(_ context.Context, workspace, name string, opts ...v1.DeleteOptions) (*types.DeletionResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - _, existed := c.store.DeleteAndGet(workspace, name) - return deletionResult(existed, "", opts) -} - -func validateSandboxWorkloadTemplate(template *types.SandboxWorkloadTemplate) error { - if template.Name == "" { - return &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox_template.metadata.name is required"} - } - if !isDNS1123Label(template.Name) { - return &types.StatusError{Code: types.ErrorInvalidArgument, Message: "template.metadata.name must be a DNS-1123 label"} - } - if template.Spec.Workload == nil { - return &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox template workload is required"} - } - if err := validateSandboxWorkloadEnvironment(template.Spec.Workload.Environment); err != nil { - return err - } - if resources := template.Spec.Workload.Resources; resources != nil && resources.GPU != nil && resources.GPU.Count != nil && *resources.GPU.Count == 0 { - return &types.StatusError{Code: types.ErrorInvalidArgument, Message: "gpu count must be greater than 0"} - } - return nil -} - -func validateSandboxWorkloadEnvironment(environment map[string]string) error { - for key := range environment { - if !isValidEnvKey(key) { - return &types.StatusError{Code: types.ErrorInvalidArgument, Message: "spec.template.environment keys must match ^[A-Za-z_][A-Za-z0-9_]*$"} - } - if len(key) >= len("OPENSHELL_") && key[:len("OPENSHELL_")] == "OPENSHELL_" { - return &types.StatusError{Code: types.ErrorInvalidArgument, Message: "spec.template.environment keys starting with OPENSHELL_ are reserved"} - } - } - return nil -} - -func filterSandboxWorkloadTemplatesByLabelSelector( - templates []*types.SandboxWorkloadTemplate, - selector string, -) ([]*types.SandboxWorkloadTemplate, error) { - selector = strings.TrimSpace(selector) - if selector == "" { - return templates, nil - } - labels, err := parseSimpleLabelSelector(selector) - if err != nil { - return nil, err - } - filtered := make([]*types.SandboxWorkloadTemplate, 0, len(templates)) - for _, template := range templates { - if labelsMatchSelector(template.Labels, labels) { - filtered = append(filtered, template) - } - } - return filtered, nil -} - -func parseSimpleLabelSelector(selector string) (map[string]string, error) { - labels := make(map[string]string) - for _, part := range strings.Split(selector, ",") { - part = strings.TrimSpace(part) - if part == "" { - continue - } - key, value, ok := strings.Cut(part, "=") - if !ok || strings.TrimSpace(key) == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "label selector must use key=value pairs"} - } - labels[strings.TrimSpace(key)] = strings.TrimSpace(value) - } - return labels, nil -} - -func labelsMatchSelector(labels map[string]string, selector map[string]string) bool { - for key, value := range selector { - if labels[key] != value { - return false - } - } - return true -} - -func compareSandboxWorkloadTemplatesForList(a, b *types.SandboxWorkloadTemplate) int { - if a.CreatedAt.Before(b.CreatedAt) { - return -1 - } - if a.CreatedAt.After(b.CreatedAt) { - return 1 - } - if a.Name < b.Name { - return -1 - } - if a.Name > b.Name { - return 1 - } - if a.Workspace < b.Workspace { - return -1 - } - if a.Workspace > b.Workspace { - return 1 - } - return 0 -} - -func isDNS1123Label(name string) bool { - if len(name) == 0 || len(name) > 63 || name[0] == '-' || name[len(name)-1] == '-' { - return false - } - previousHyphen := false - for i := 0; i < len(name); i++ { - b := name[i] - if b == '-' { - if previousHyphen { - return false - } - previousHyphen = true - continue - } - previousHyphen = false - if !isASCIIDigit(b) && (b < 'a' || b > 'z') { - return false - } - } - return true -} - -func isValidEnvKey(key string) bool { - if key == "" { - return false - } - for i := 0; i < len(key); i++ { - b := key[i] - if i == 0 && b != '_' && !isASCIIAlpha(b) { - return false - } - if i > 0 && b != '_' && !isASCIIAlpha(b) && !isASCIIDigit(b) { - return false - } - } - return true -} - -func isASCIIAlpha(b byte) bool { - return (b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z') -} - -func isASCIIDigit(b byte) bool { - return b >= '0' && b <= '9' -} diff --git a/sdk/go/openshell/v1/fake/sandbox_template_test.go b/sdk/go/openshell/v1/fake/sandbox_template_test.go deleted file mode 100644 index 9b4c4b5e88..0000000000 --- a/sdk/go/openshell/v1/fake/sandbox_template_test.go +++ /dev/null @@ -1,468 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func newTestSandboxTemplateClient() *fakeSandboxTemplateClient { - store := newobjectStore(sandboxWorkloadTemplate, copySandboxWorkloadTemplate) - return newFakeSandboxTemplateClient(store, func() bool { return false }) -} - -func testSandboxWorkloadTemplate(name string) *types.SandboxWorkloadTemplate { - return &types.SandboxWorkloadTemplate{ - Name: name, - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{Image: "registry.example.com/agent:latest"}, - }, - } -} - -func TestIsDNS1123Label(t *testing.T) { - tests := map[string]bool{ - "": false, - "gpu-kata": true, - "Invalid_Template_Name": false, - "-gpu": false, - "gpu-": false, - "gpu--kata": false, - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": false, - } - - for name, want := range tests { - assert.Equal(t, want, isDNS1123Label(name), "name %q", name) - } -} - -func TestSandboxTemplate_CreateGetListDelete(t *testing.T) { - tc := newTestSandboxTemplateClient() - ctx := context.Background() - - template := &types.SandboxWorkloadTemplate{ - Name: "gpu-kata", - Labels: map[string]string{"team": "platform"}, - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{Image: "python:3.12"}, - DriverConfig: map[string]any{"kubernetes": map[string]any{"runtime_class_name": "kata"}}, - DesiredServiceLevel: &types.SandboxServiceLevel{ - Startup: &types.SandboxStartup{ReadyWithin: 30 * time.Second, MaxBurst: 4}, - }, - }, - } - - created, err := tc.Create(ctx, "default", template) - require.NoError(t, err) - assert.Equal(t, "gpu-kata", created.Name) - assert.Equal(t, "default", created.Workspace) - assert.Equal(t, uint64(1), created.ResourceVersion) - assert.NotZero(t, created.CreatedAt) - - got, err := tc.Get(ctx, "default", "gpu-kata") - require.NoError(t, err) - assert.Equal(t, "python:3.12", got.Spec.Workload.Image) - assert.Equal(t, "kata", got.Spec.DriverConfig["kubernetes"].(map[string]any)["runtime_class_name"]) - - listed, err := tc.ListAll(ctx, "default") - require.NoError(t, err) - require.Len(t, listed, 1) - assert.Equal(t, "gpu-kata", listed[0].Name) - - deleted, err := tc.Delete(ctx, "default", "gpu-kata") - require.NoError(t, err) - assert.Equal(t, types.DeletionCompleted, deleted.Outcome) - _, err = tc.Get(ctx, "default", "gpu-kata") - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) - - deleted, err = tc.Delete(ctx, "default", "gpu-kata", types.DeleteOptions{AllowMissing: true}) - require.NoError(t, err) - assert.Equal(t, types.DeletionAlreadyAbsent, deleted.Outcome) -} - -func TestSandboxTemplate_CreateAlreadyExists(t *testing.T) { - tc := newTestSandboxTemplateClient() - ctx := context.Background() - - template := testSandboxWorkloadTemplate("gpu-kata") - _, err := tc.Create(ctx, "default", template) - require.NoError(t, err) - - _, err = tc.Create(ctx, "default", template) - require.Error(t, err) - assert.True(t, types.IsAlreadyExists(err)) -} - -func TestSandboxTemplate_ListAllWorkspaces(t *testing.T) { - tc := newTestSandboxTemplateClient() - ctx := context.Background() - - _, _ = tc.Create(ctx, "default", testSandboxWorkloadTemplate("default-template")) - _, _ = tc.Create(ctx, "team-a", testSandboxWorkloadTemplate("team-template")) - - listed, err := tc.ListAll(ctx, "default", types.ListOptions{AllWorkspaces: true}) - require.NoError(t, err) - assert.Len(t, listed, 2) -} - -func TestSandboxTemplate_ListFiltersByLabelSelector(t *testing.T) { - tc := newTestSandboxTemplateClient() - ctx := context.Background() - - _, _ = tc.Create(ctx, "default", &types.SandboxWorkloadTemplate{ - Name: "runtime-template", - Labels: map[string]string{"team": "runtime"}, - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{Image: "python:3.12"}, - }, - }) - _, _ = tc.Create(ctx, "default", &types.SandboxWorkloadTemplate{ - Name: "batch-template", - Labels: map[string]string{"team": "batch"}, - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{Image: "python:3.12"}, - }, - }) - - listed, err := tc.ListAll(ctx, "default", types.ListOptions{LabelSelector: "team=runtime"}) - - require.NoError(t, err) - require.Len(t, listed, 1) - assert.Equal(t, "runtime-template", listed[0].Name) -} - -func TestSandboxTemplate_ListRejectsNegativePagination(t *testing.T) { - tc := newTestSandboxTemplateClient() - ctx := context.Background() - - _, err := tc.ListAll(ctx, "default", types.ListOptions{PageSize: -1}) - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestSandboxTemplate_ListReturnsAllFilteredResults(t *testing.T) { - tc := newTestSandboxTemplateClient() - ctx := context.Background() - - _, _ = tc.Create(ctx, "default", &types.SandboxWorkloadTemplate{ - Name: "runtime-a", - Labels: map[string]string{"team": "runtime"}, - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{Image: "python:3.12"}, - }, - }) - _, _ = tc.Create(ctx, "default", &types.SandboxWorkloadTemplate{ - Name: "batch-a", - Labels: map[string]string{"team": "batch"}, - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{Image: "python:3.12"}, - }, - }) - _, _ = tc.Create(ctx, "default", &types.SandboxWorkloadTemplate{ - Name: "runtime-b", - Labels: map[string]string{"team": "runtime"}, - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{Image: "python:3.12"}, - }, - }) - - listed, err := tc.ListAll(ctx, "default", types.ListOptions{ - LabelSelector: "team=runtime", - PageSize: 1, - }) - - require.NoError(t, err) - require.Len(t, listed, 2) - assert.Equal(t, "runtime-a", listed[0].Name) - assert.Equal(t, "runtime-b", listed[1].Name) -} - -func TestSandboxTemplate_CreateSandboxFromTemplateRequiresExistingTemplate(t *testing.T) { - client := NewClient() - ctx := context.Background() - - _, err := client.CreateSandboxFromTemplate(ctx, "default", "job-1", "missing", nil, nil) - - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestSandboxTemplate_CreateSandboxFromTemplateResolvesWorkloadAndGovernance(t *testing.T) { - client := NewClient() - ctx := context.Background() - gpuCount := uint32(1) - policy := &types.SandboxPolicy{ - Version: 1, - NetworkPolicies: map[string]types.NetworkPolicyRule{ - "api": {Name: "api"}, - }, - } - client.AddSandboxTemplate("default", &types.SandboxWorkloadTemplate{ - Name: "gpu-kata", - ResourceVersion: 7, - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{ - Image: "registry.example.com/agent:latest", - Environment: map[string]string{"FEATURE_FLAG": "on"}, - Resources: &types.SandboxResources{ - CPU: "2", - Memory: "4Gi", - GPU: &types.SandboxGPURequirements{Count: &gpuCount}, - }, - }, - DriverConfig: map[string]any{ - "kubernetes": map[string]any{"runtime_class_name": "kata-containers"}, - }, - }, - }) - - created, err := client.CreateSandboxFromTemplate( - ctx, - "default", - "job-1", - "gpu-kata", - &types.SandboxSpec{ - Providers: []string{"github"}, - Policy: policy, - }, - types.WithLabels(map[string]string{"team": "runtime"}), - ) - - require.NoError(t, err) - assert.Equal(t, "job-1", created.Name) - assert.Equal(t, map[string]string{"team": "runtime"}, created.Labels) - assert.Equal(t, map[string]string{"FEATURE_FLAG": "on"}, created.Spec.Environment) - require.NotNil(t, created.Spec.Template) - assert.Equal(t, "registry.example.com/agent:latest", created.Spec.Template.Image) - assert.Equal(t, map[string]any{"limits": map[string]any{"cpu": "2", "memory": "4Gi"}}, created.Spec.Template.Resources) - assert.Equal(t, "kata-containers", created.Spec.Template.DriverConfig["kubernetes"].(map[string]any)["runtime_class_name"]) - assert.True(t, created.Spec.GPU) - require.NotNil(t, created.Spec.GPUCount) - assert.Equal(t, uint32(1), *created.Spec.GPUCount) - assert.Equal(t, []string{"github"}, created.Spec.Providers) - require.NotNil(t, created.Spec.Policy) - assert.Equal(t, uint32(1), created.Spec.Policy.Version) - require.NotNil(t, created.CreatedFromWorkloadTemplate) - assert.Equal(t, "gpu-kata", created.CreatedFromWorkloadTemplate.Name) - assert.Equal(t, "7", created.CreatedFromWorkloadTemplate.ResourceVersion) -} - -func TestSandboxTemplate_CreateSandboxFromTemplateRejectsWorkloadOverrides(t *testing.T) { - client := NewClient() - ctx := context.Background() - gpuCount := uint32(1) - client.AddSandboxTemplate("default", &types.SandboxWorkloadTemplate{ - Name: "gpu-kata", - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{Image: "registry.example.com/agent:latest"}, - }, - }) - - tests := map[string]*types.SandboxSpec{ - "log_level": { - LogLevel: "debug", - }, - "environment": { - Environment: map[string]string{"FEATURE_FLAG": "off"}, - }, - "template": { - Template: &types.SandboxTemplate{Image: "registry.example.com/override:latest"}, - }, - "gpu_count": { - GPUCount: &gpuCount, - }, - "gpu": { - GPU: true, - }, - } - - for name, spec := range tests { - t.Run(name, func(t *testing.T) { - _, err := client.CreateSandboxFromTemplate(ctx, "default", "job-"+name, "gpu-kata", spec, nil) - - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) - }) - } - - listed, err := client.Sandboxes().ListAll(ctx, "default") - require.NoError(t, err) - assert.Empty(t, listed) -} - -func TestSandboxTemplate_DefaultGpuRequestRoundTripsTemplate(t *testing.T) { - tc := newTestSandboxTemplateClient() - ctx := context.Background() - - created, err := tc.Create(ctx, "default", &types.SandboxWorkloadTemplate{ - Name: "default-gpu", - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{ - Resources: &types.SandboxResources{ - GPU: &types.SandboxGPURequirements{}, - }, - }, - }, - }) - - require.NoError(t, err) - require.NotNil(t, created.Spec.Workload.Resources.GPU) - assert.Nil(t, created.Spec.Workload.Resources.GPU.Count) - - got, err := tc.Get(ctx, "default", "default-gpu") - require.NoError(t, err) - require.NotNil(t, got.Spec.Workload.Resources.GPU) - assert.Nil(t, got.Spec.Workload.Resources.GPU.Count) -} - -func TestSandboxTemplate_CreateSandboxFromTemplatePreservesDefaultGPURequest(t *testing.T) { - client := NewClient() - ctx := context.Background() - - client.AddSandboxTemplate("default", &types.SandboxWorkloadTemplate{ - Name: "default-gpu", - ResourceVersion: 3, - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{ - Image: "registry.example.com/agent:latest", - Resources: &types.SandboxResources{ - GPU: &types.SandboxGPURequirements{}, - }, - }, - }, - }) - - created, err := client.CreateSandboxFromTemplate( - ctx, - "default", - "job-default-gpu", - "default-gpu", - nil, - ) - - require.NoError(t, err) - assert.True(t, created.Spec.GPU) - assert.Nil(t, created.Spec.GPUCount) -} - -func TestSandboxTemplate_DeepCopy(t *testing.T) { - tc := newTestSandboxTemplateClient() - ctx := context.Background() - - template := &types.SandboxWorkloadTemplate{ - Name: "gpu-kata", - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{ - Image: "python:3.12", - Environment: map[string]string{"KEY": "value"}, - }, - DriverConfig: map[string]any{"kubernetes": map[string]any{"runtime_class_name": "kata"}}, - }, - } - - created, err := tc.Create(ctx, "default", template) - require.NoError(t, err) - - template.Spec.Workload.Image = "mutated" - template.Spec.Workload.Environment["KEY"] = "mutated" - template.Spec.DriverConfig["kubernetes"].(map[string]any)["runtime_class_name"] = "mutated" - created.Spec.Workload.Image = "mutated-return" - - got, err := tc.Get(ctx, "default", "gpu-kata") - require.NoError(t, err) - assert.Equal(t, "python:3.12", got.Spec.Workload.Image) - assert.Equal(t, "value", got.Spec.Workload.Environment["KEY"]) - assert.Equal(t, "kata", got.Spec.DriverConfig["kubernetes"].(map[string]any)["runtime_class_name"]) -} - -func TestSandboxTemplate_CreateNil(t *testing.T) { - tc := newTestSandboxTemplateClient() - ctx := context.Background() - - _, err := tc.Create(ctx, "default", nil) - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestSandboxTemplate_CreateRejectsInvalidTemplate(t *testing.T) { - tc := newTestSandboxTemplateClient() - ctx := context.Background() - zeroGPUCount := uint32(0) - - tests := map[string]*types.SandboxWorkloadTemplate{ - "missing_name": { - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{Image: "registry.example.com/agent:latest"}, - }, - }, - "invalid_name": { - Name: "Invalid_Template_Name", - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{Image: "registry.example.com/agent:latest"}, - }, - }, - "missing_workload": { - Name: "missing-workload", - }, - "invalid_environment": { - Name: "invalid-env", - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{ - Image: "registry.example.com/agent:latest", - Environment: map[string]string{"BAD KEY": "value"}, - }, - }, - }, - "reserved_environment": { - Name: "reserved-env", - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{ - Image: "registry.example.com/agent:latest", - Environment: map[string]string{"OPENSHELL_TOKEN": "value"}, - }, - }, - }, - "zero_gpu": { - Name: "zero-gpu", - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{ - Image: "registry.example.com/agent:latest", - Resources: &types.SandboxResources{ - GPU: &types.SandboxGPURequirements{Count: &zeroGPUCount}, - }, - }, - }, - }, - } - - for name, template := range tests { - t.Run(name, func(t *testing.T) { - _, err := tc.Create(ctx, "default", template) - - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) - }) - } - - listed, err := tc.ListAll(ctx, "default") - require.NoError(t, err) - assert.Empty(t, listed) -} - -func TestSandboxTemplate_ClosedReturnsUnavailable(t *testing.T) { - store := newobjectStore(sandboxWorkloadTemplate, copySandboxWorkloadTemplate) - tc := newFakeSandboxTemplateClient(store, func() bool { return true }) - ctx := context.Background() - - _, err := tc.Create(ctx, "default", &types.SandboxWorkloadTemplate{Name: "gpu-kata"}) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} diff --git a/sdk/go/openshell/v1/fake/sandbox_test.go b/sdk/go/openshell/v1/fake/sandbox_test.go deleted file mode 100644 index 3f4419ba2f..0000000000 --- a/sdk/go/openshell/v1/fake/sandbox_test.go +++ /dev/null @@ -1,1039 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "fmt" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// helper to build a minimal fake sandbox client for testing. -func newTestSandboxClient() *fakeSandboxClient { - store := newobjectStore(sandboxName, copySandbox) - templateStore := newobjectStore(sandboxWorkloadTemplate, copySandboxWorkloadTemplate) - broadcaster := newWatchBroadcaster[*types.Sandbox]() - return newFakeSandboxClient(store, templateStore, broadcaster, func() bool { return false }) -} - -// --- T008: Sandbox CRUD tests --- - -func TestSandbox_Create(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sb, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{LogLevel: "debug"}, types.WithLabels(map[string]string{"env": "test"})) - require.NoError(t, err) - assert.Equal(t, "test-sb", sb.Name) - assert.Equal(t, "debug", sb.Spec.LogLevel) - assert.Equal(t, "test", sb.Labels["env"]) - assert.Equal(t, types.SandboxProvisioning, sb.Status.Phase) - assert.NotZero(t, sb.CreatedAt) - assert.Equal(t, uint64(1), sb.ResourceVersion) -} - -func TestSandbox_Create_AlreadyExists(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - _, err = sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.Error(t, err) - assert.True(t, types.IsAlreadyExists(err)) -} - -// Before functional options, an unlabeled sandbox was created by passing nil -// for the positional labels parameter. Such calls still compile against the new -// variadic signature, so they must keep working instead of panicking. -func TestSandbox_Create_LegacyTrailingNil(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sb, err := sc.Create(ctx, "default", "legacy-sb", &types.SandboxSpec{LogLevel: "debug"}, nil) - require.NoError(t, err) - assert.Equal(t, "legacy-sb", sb.Name) - assert.Empty(t, sb.Labels) - assert.Empty(t, sb.Annotations) -} - -func TestSandbox_CreateFromTemplate_LegacyTrailingNil(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sc.templateStore.Insert("default", &types.SandboxWorkloadTemplate{ - Name: "gpu-kata", - ResourceVersion: 1, - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{ - Image: "registry.example.com/agent:latest", - }, - }, - }) - - sb, err := sc.CreateFromTemplate(ctx, "default", "legacy-tpl", "gpu-kata", &types.SandboxSpec{}, nil) - require.NoError(t, err) - assert.Equal(t, "legacy-tpl", sb.Name) - assert.Empty(t, sb.Labels) -} - -func TestSandbox_Create_WithAnnotations(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sb, err := sc.Create(ctx, "default", "annotated", &types.SandboxSpec{}, - types.WithAnnotations(map[string]string{"source": "cli", "user": "admin"})) - require.NoError(t, err) - assert.Equal(t, "cli", sb.Annotations["source"]) - assert.Equal(t, "admin", sb.Annotations["user"]) - - got, err := sc.Get(ctx, "default", "annotated") - require.NoError(t, err) - assert.Equal(t, "cli", got.Annotations["source"]) -} - -func TestSandbox_Create_WithAnnotationsDeepCopy(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - input := map[string]string{"key": "original"} - sb, err := sc.Create(ctx, "default", "dc-test", &types.SandboxSpec{}, - types.WithAnnotations(input)) - require.NoError(t, err) - - input["key"] = "MUTATED" - assert.Equal(t, "original", sb.Annotations["key"], "annotations must be deep copied") -} - -func TestSandbox_Create_NoAnnotations(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sb, err := sc.Create(ctx, "default", "no-ann", &types.SandboxSpec{}) - require.NoError(t, err) - assert.Nil(t, sb.Annotations) -} - -func TestCopyAnyMap(t *testing.T) { - t.Run("nil", func(t *testing.T) { - assert.Nil(t, copyAnyMap(nil)) - }) - - t.Run("flat", func(t *testing.T) { - original := map[string]any{"cpu": "2", "memory": "4Gi"} - copied := copyAnyMap(original) - assert.Equal(t, original, copied) - - original["cpu"] = "MUTATED" - assert.Equal(t, "2", copied["cpu"]) - }) - - t.Run("nested map", func(t *testing.T) { - original := map[string]any{ - "limits": map[string]any{"cpu": "4", "memory": "8Gi"}, - } - copied := copyAnyMap(original) - - nested := original["limits"].(map[string]any) - nested["cpu"] = "MUTATED" - - copiedNested := copied["limits"].(map[string]any) - assert.Equal(t, "4", copiedNested["cpu"]) - }) - - t.Run("nested slice", func(t *testing.T) { - original := map[string]any{ - "ports": []any{float64(80), float64(443)}, - } - copied := copyAnyMap(original) - - original["ports"].([]any)[0] = float64(9999) - assert.Equal(t, float64(80), copied["ports"].([]any)[0]) - }) - - t.Run("scalar types", func(t *testing.T) { - original := map[string]any{ - "str": "hello", "num": float64(42), "flag": true, "null": nil, - } - copied := copyAnyMap(original) - assert.Equal(t, original, copied) - }) -} - -func TestCopySandboxTemplate_ResourcesDeepCopy(t *testing.T) { - tmpl := types.SandboxTemplate{ - Image: "img:v1", - Resources: map[string]any{"cpu": "2", "nested": map[string]any{"key": "val"}}, - DriverConfig: map[string]any{"runtime": "kata"}, - } - - copied := copySandboxTemplate(tmpl) - - tmpl.Resources["cpu"] = "MUTATED" - assert.Equal(t, "2", copied.Resources["cpu"]) - - tmpl.DriverConfig["runtime"] = "MUTATED" - assert.Equal(t, "kata", copied.DriverConfig["runtime"]) - - nested := tmpl.Resources["nested"].(map[string]any) - nested["key"] = "MUTATED" - copiedNested := copied.Resources["nested"].(map[string]any) - assert.Equal(t, "val", copiedNested["key"]) -} - -func TestSandbox_Create_NilSpec(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sb, err := sc.Create(ctx, "default", "test-sb", nil) - require.NoError(t, err) - assert.Equal(t, "test-sb", sb.Name) -} - -func TestSandbox_Get(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{LogLevel: "info"}) - require.NoError(t, err) - - got, err := sc.Get(ctx, "default", "test-sb") - require.NoError(t, err) - assert.Equal(t, "test-sb", got.Name) - assert.Equal(t, "info", got.Spec.LogLevel) -} - -func TestSandbox_Get_NotFound(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, err := sc.Get(ctx, "default", "nonexistent") - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestSandbox_List_Empty(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - list, err := sc.ListAll(ctx, "default") - require.NoError(t, err) - assert.Empty(t, list) -} - -func TestSandbox_List(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, _ = sc.Create(ctx, "default", "sb-1", &types.SandboxSpec{}) - _, _ = sc.Create(ctx, "default", "sb-2", &types.SandboxSpec{}) - - list, err := sc.ListAll(ctx, "default") - require.NoError(t, err) - assert.Len(t, list, 2) -} - -func TestSandbox_Delete(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, _ = sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - - _, err := sc.Delete(ctx, "default", "test-sb") - require.NoError(t, err) - - _, err = sc.Get(ctx, "default", "test-sb") - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestSandbox_Delete_Idempotent(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - // Explicitly allow a missing target. - _, err := sc.Delete(ctx, "default", "nonexistent", types.DeleteOptions{AllowMissing: true}) - require.NoError(t, err) -} - -func TestSandbox_DeepCopy_OnCreate(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - labels := map[string]string{"env": "test"} - spec := &types.SandboxSpec{ - LogLevel: "debug", - Environment: map[string]string{"KEY": "value"}, - } - - sb, err := sc.Create(ctx, "default", "test-sb", spec, types.WithLabels(labels)) - require.NoError(t, err) - - // Mutating inputs should not affect stored object - labels["env"] = "mutated" - spec.LogLevel = "mutated" - spec.Environment["KEY"] = "mutated" - - got, err := sc.Get(ctx, "default", "test-sb") - require.NoError(t, err) - assert.Equal(t, "test", got.Labels["env"]) - assert.Equal(t, "debug", got.Spec.LogLevel) - assert.Equal(t, "value", got.Spec.Environment["KEY"]) - - // Mutating returned object should not affect stored object - sb.Labels["env"] = "mutated-return" - got2, err := sc.Get(ctx, "default", "test-sb") - require.NoError(t, err) - assert.Equal(t, "test", got2.Labels["env"]) -} - -func TestSandbox_DeepCopy_OnGet(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, _ = sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{ - Environment: map[string]string{"KEY": "value"}, - }) - - got, err := sc.Get(ctx, "default", "test-sb") - require.NoError(t, err) - - got.Spec.Environment["KEY"] = "mutated" - - got2, err := sc.Get(ctx, "default", "test-sb") - require.NoError(t, err) - assert.Equal(t, "value", got2.Spec.Environment["KEY"]) -} - -// --- T009: WaitReady tests --- - -func TestSandbox_WaitReady(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - sb, err := sc.WaitReady(ctx, "default", "test-sb") - require.NoError(t, err) - assert.Equal(t, types.SandboxReady, sb.Status.Phase) - - // Verify the store is also updated - got, err := sc.Get(ctx, "default", "test-sb") - require.NoError(t, err) - assert.Equal(t, types.SandboxReady, got.Status.Phase) -} - -func TestSandbox_WaitReady_NotFound(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, err := sc.WaitReady(ctx, "default", "nonexistent") - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestSandbox_WaitReady_ContextCancellation(t *testing.T) { - sc := newTestSandboxClient() - - _, err := sc.Create(context.Background(), "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - ctx, cancel := context.WithCancel(context.Background()) - cancel() // Cancel immediately - - _, err = sc.WaitReady(ctx, "default", "test-sb") - require.Error(t, err) - // Should return a context error, not a status error - assert.ErrorIs(t, err, context.Canceled) -} - -func TestSandbox_WaitReady_ContextDeadlineExceeded(t *testing.T) { - sc := newTestSandboxClient() - - _, err := sc.Create(context.Background(), "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-time.Second)) - defer cancel() - - _, err = sc.WaitReady(ctx, "default", "test-sb") - require.Error(t, err) - assert.True(t, types.IsDeadlineExceeded(err), "WaitReady must wrap context.DeadlineExceeded in StatusError") -} - -func TestSandbox_WaitReady_AlreadyReady(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, _ = sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - - // Make it ready - _, err := sc.WaitReady(ctx, "default", "test-sb") - require.NoError(t, err) - - // WaitReady on an already-ready sandbox should return immediately - sb, err := sc.WaitReady(ctx, "default", "test-sb") - require.NoError(t, err) - assert.Equal(t, types.SandboxReady, sb.Status.Phase) -} - -func TestSandbox_WaitReady_IncrementsResourceVersion(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - created, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - initialVersion := created.ResourceVersion - - ready, err := sc.WaitReady(ctx, "default", "test-sb") - require.NoError(t, err) - assert.Greater(t, ready.ResourceVersion, initialVersion) -} - -func TestSandbox_WaitReady_ContextTimeout(t *testing.T) { - sc := newTestSandboxClient() - - _, err := sc.Create(context.Background(), "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) - defer cancel() - - // Override the sandbox phase to Error so WaitReady doesn't auto-transition - // Actually, with our simple fake, WaitReady transitions immediately unless context is done. - // So just test the context-cancelled path: - cancel() - _, err = sc.WaitReady(ctx, "default", "test-sb") - require.Error(t, err) -} - -// --- T011: Watch tests --- - -func TestSandbox_Watch_AddedOnCreate(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - w, err := sc.Watch(ctx, "default", "") - require.NoError(t, err) - defer w.Stop() - - _, err = sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{LogLevel: "info"}) - require.NoError(t, err) - - select { - case ev := <-w.ResultChan(): - assert.Equal(t, types.EventAdded, ev.Type) - assert.Equal(t, "test-sb", ev.Object.Name) - assert.Equal(t, "info", ev.Object.Spec.LogLevel) - case <-time.After(time.Second): - t.Fatal("timed out waiting for ADDED event") - } -} - -func TestSandbox_Watch_DeletedOnDelete(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, _ = sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - - w, err := sc.Watch(ctx, "default", "") - require.NoError(t, err) - defer w.Stop() - - _, err = sc.Delete(ctx, "default", "test-sb") - require.NoError(t, err) - - select { - case ev := <-w.ResultChan(): - assert.Equal(t, types.EventDeleted, ev.Type) - assert.Equal(t, "test-sb", ev.Object.Name) - case <-time.After(time.Second): - t.Fatal("timed out waiting for DELETED event") - } -} - -func TestSandbox_Watch_ModifiedOnWaitReady(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, _ = sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - - w, err := sc.Watch(ctx, "default", "") - require.NoError(t, err) - defer w.Stop() - - _, err = sc.WaitReady(ctx, "default", "test-sb") - require.NoError(t, err) - - select { - case ev := <-w.ResultChan(): - assert.Equal(t, types.EventModified, ev.Type) - assert.Equal(t, types.SandboxReady, ev.Object.Status.Phase) - case <-time.After(time.Second): - t.Fatal("timed out waiting for MODIFIED event") - } -} - -func TestSandbox_Watch_NameFiltering(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - // Watch only "alpha" - w, err := sc.Watch(ctx, "default", "alpha") - require.NoError(t, err) - defer w.Stop() - - // Create "beta" — should not be received - _, _ = sc.Create(ctx, "default", "beta", &types.SandboxSpec{}) - - // Create "alpha" — should be received - _, _ = sc.Create(ctx, "default", "alpha", &types.SandboxSpec{}) - - select { - case ev := <-w.ResultChan(): - assert.Equal(t, types.EventAdded, ev.Type) - assert.Equal(t, "alpha", ev.Object.Name) - case <-time.After(time.Second): - t.Fatal("timed out waiting for filtered event") - } -} - -func TestSandbox_Watch_MultipleWatchers(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - w1, err := sc.Watch(ctx, "default", "") - require.NoError(t, err) - defer w1.Stop() - - w2, err := sc.Watch(ctx, "default", "") - require.NoError(t, err) - defer w2.Stop() - - _, _ = sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - - for _, w := range []types.WatchInterface[*types.Sandbox]{w1, w2} { - select { - case ev := <-w.ResultChan(): - assert.Equal(t, types.EventAdded, ev.Type) - assert.Equal(t, "test-sb", ev.Object.Name) - case <-time.After(time.Second): - t.Fatal("timed out waiting for event on watcher") - } - } -} - -func TestSandbox_Watch_StopClosesChannel(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - w, err := sc.Watch(ctx, "default", "") - require.NoError(t, err) - - w.Stop() - - _, ok := <-w.ResultChan() - assert.False(t, ok, "channel should be closed after Stop") -} - -func TestSandbox_Watch_DeletedEventContainsFullObject(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, _ = sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{LogLevel: "debug"}, types.WithLabels(map[string]string{"env": "test"})) - - w, err := sc.Watch(ctx, "default", "") - require.NoError(t, err) - defer w.Stop() - - _, _ = sc.Delete(ctx, "default", "test-sb") - - select { - case ev := <-w.ResultChan(): - assert.Equal(t, types.EventDeleted, ev.Type) - // Verify the DELETED event contains the full last-known object - assert.Equal(t, "debug", ev.Object.Spec.LogLevel) - assert.Equal(t, "test", ev.Object.Labels["env"]) - case <-time.After(time.Second): - t.Fatal("timed out waiting for DELETED event") - } -} - -// --- T019: Concurrent sandbox access tests --- - -func TestSandbox_ConcurrentCreateGetDeleteWatch(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - const goroutines = 10 - const opsPerGoroutine = 20 - - // Start a watcher to exercise broadcast under concurrency - w, err := sc.Watch(ctx, "default", "") - require.NoError(t, err) - defer w.Stop() - - // Drain watcher events in a background goroutine - done := make(chan struct{}) - go func() { - defer close(done) - for range w.ResultChan() { //nolint:revive // intentionally draining channel - } - }() - - var wg sync.WaitGroup - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - for j := 0; j < opsPerGoroutine; j++ { - name := fmt.Sprintf("sb-%d-%d", id, j) - _, _ = sc.Create(ctx, "default", name, &types.SandboxSpec{LogLevel: "info"}) - _, _ = sc.Get(ctx, "default", name) - _, _ = sc.ListAll(ctx, "default") - _, _ = sc.WaitReady(ctx, "default", name) - _, _ = sc.Delete(ctx, "default", name) - } - }(i) - } - wg.Wait() - - // Stop watcher and wait for drain goroutine - w.Stop() - <-done -} - -// --- T026: AttachProvider / DetachProvider / ListProviders tests --- - -func TestSandbox_AttachProvider(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sb, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - result, err := sc.AttachProvider(ctx, "default", "test-sb", "openai", sb.ResourceVersion) - require.NoError(t, err) - assert.True(t, result.Attached) - assert.Equal(t, "test-sb", result.Sandbox.Name) - assert.Contains(t, result.Sandbox.Spec.Providers, "openai") -} - -func TestSandbox_AttachProvider_AlreadyAttached(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sb, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - result, err := sc.AttachProvider(ctx, "default", "test-sb", "openai", sb.ResourceVersion) - require.NoError(t, err) - assert.True(t, result.Attached) - - // Attach again — should return Attached=false (idempotent, already attached) - result2, err := sc.AttachProvider(ctx, "default", "test-sb", "openai", result.Sandbox.ResourceVersion) - require.NoError(t, err) - assert.False(t, result2.Attached) -} - -func TestSandbox_AttachProvider_SandboxNotFound(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, err := sc.AttachProvider(ctx, "default", "nonexistent", "openai", 0) - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestSandbox_DetachProvider(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sb, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - result, err := sc.AttachProvider(ctx, "default", "test-sb", "openai", sb.ResourceVersion) - require.NoError(t, err) - - detach, err := sc.DetachProvider(ctx, "default", "test-sb", "openai", result.Sandbox.ResourceVersion) - require.NoError(t, err) - assert.True(t, detach.Detached) - assert.NotContains(t, detach.Sandbox.Spec.Providers, "openai") -} - -func TestSandbox_DetachProvider_NotAttached(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sb, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - result, err := sc.DetachProvider(ctx, "default", "test-sb", "openai", sb.ResourceVersion) - require.NoError(t, err) - assert.False(t, result.Detached) -} - -func TestSandbox_DetachProvider_SandboxNotFound(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, err := sc.DetachProvider(ctx, "default", "nonexistent", "openai", 0) - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestSandbox_ListProviders(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sb, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - // No providers yet - providers, err := sc.ListAllProviders(ctx, "default", "test-sb") - require.NoError(t, err) - assert.Empty(t, providers) - - // Attach two providers - result, err := sc.AttachProvider(ctx, "default", "test-sb", "openai", sb.ResourceVersion) - require.NoError(t, err) - - _, err = sc.AttachProvider(ctx, "default", "test-sb", "anthropic", result.Sandbox.ResourceVersion) - require.NoError(t, err) - - pager, err := sc.ListProviders("default", "test-sb", v1.ListOptions{PageSize: 1}) - require.NoError(t, err) - firstPage, err := pager.NextPage(ctx) - require.NoError(t, err) - require.Len(t, firstPage.Items, 1) - require.NotEmpty(t, firstPage.NextPageToken) - - providers, err = sc.ListAllProviders(ctx, "default", "test-sb") - require.NoError(t, err) - assert.Len(t, providers, 2) - - names := make([]string, len(providers)) - for i, p := range providers { - names[i] = p.Name - } - assert.Contains(t, names, "openai") - assert.Contains(t, names, "anthropic") -} - -func TestSandbox_ListProviders_SandboxNotFound(t *testing.T) { - sc := newTestSandboxClient() - - _, err := sc.ListProviders("default", "nonexistent") - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestSandbox_AttachProvider_BroadcastsModified(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sb, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - w, err := sc.Watch(ctx, "default", "") - require.NoError(t, err) - defer w.Stop() - - _, err = sc.AttachProvider(ctx, "default", "test-sb", "openai", sb.ResourceVersion) - require.NoError(t, err) - - select { - case ev := <-w.ResultChan(): - assert.Equal(t, types.EventModified, ev.Type) - assert.Contains(t, ev.Object.Spec.Providers, "openai") - case <-time.After(time.Second): - t.Fatal("timed out waiting for MODIFIED event from AttachProvider") - } -} - -// --- T033: StopOnTerminal tests for fake Watch --- - -func TestSandbox_Watch_StopOnTerminal_Ready(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - w, err := sc.Watch(ctx, "default", "test-sb", v1.WatchOptions{StopOnTerminal: true}) - require.NoError(t, err) - - // Transition to Ready — this broadcasts a MODIFIED event with SandboxReady phase - _, err = sc.WaitReady(ctx, "default", "test-sb") - require.NoError(t, err) - - // Should receive the Ready event - var gotReady bool - for ev := range w.ResultChan() { - if ev.Object != nil && ev.Object.Status.Phase == types.SandboxReady { - gotReady = true - } - } - // Channel should be closed after the terminal event - assert.True(t, gotReady, "expected to receive a Ready event before channel closed") -} - -func TestSandbox_Watch_StopOnTerminal_Error(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - sb, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - w, err := sc.Watch(ctx, "default", "test-sb", v1.WatchOptions{StopOnTerminal: true}) - require.NoError(t, err) - - // Manually transition to Error phase via store update + broadcast - sb.Status.Phase = types.SandboxError - sb.ResourceVersion++ - updated, err := sc.store.Update("default", sb) - require.NoError(t, err) - sc.broadcaster.Broadcast(types.Event[*types.Sandbox]{ - Type: types.EventModified, - Object: copySandbox(updated), - }, "test-sb") - - // Should receive the Error event and then the channel closes - var gotError bool - for ev := range w.ResultChan() { - if ev.Object != nil && ev.Object.Status.Phase == types.SandboxError { - gotError = true - } - } - assert.True(t, gotError, "expected to receive an Error event before channel closed") -} - -func TestSandbox_Watch_StopOnTerminal_False_DoesNotClose(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, err := sc.Create(ctx, "default", "test-sb", &types.SandboxSpec{}) - require.NoError(t, err) - - // Watch WITHOUT StopOnTerminal - w, err := sc.Watch(ctx, "default", "test-sb") - require.NoError(t, err) - defer w.Stop() - - // Transition to Ready - _, err = sc.WaitReady(ctx, "default", "test-sb") - require.NoError(t, err) - - // Receive the Ready event - select { - case ev := <-w.ResultChan(): - assert.Equal(t, types.SandboxReady, ev.Object.Status.Phase) - case <-time.After(time.Second): - t.Fatal("timed out waiting for Ready event") - } - - // Channel should still be open — verify by checking no close - select { - case _, ok := <-w.ResultChan(): - if !ok { - t.Fatal("channel closed unexpectedly when StopOnTerminal was not set") - } - // Got another event, that's fine - case <-time.After(100 * time.Millisecond): - // No event and not closed — correct behavior - } -} - -// --- T016: Sandbox Create with Policy --- - -func TestFakeSandboxCreateWithPolicy(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - spec := &types.SandboxSpec{ - LogLevel: "debug", - Policy: &types.SandboxPolicy{ - Version: 3, - Filesystem: &types.FilesystemPolicy{ - IncludeWorkdir: true, - ReadOnly: []string{"/etc", "/usr/share"}, - ReadWrite: []string{"/tmp"}, - }, - Landlock: &types.LandlockPolicy{ - Compatibility: "best_effort", - }, - Process: &types.ProcessPolicy{ - RunAsUser: "sandbox", - RunAsGroup: "sandbox-group", - }, - NetworkPolicies: map[string]types.NetworkPolicyRule{ - "web": { - Name: "web", - Endpoints: []types.PolicyNetworkEndpoint{ - {Host: "api.example.com", Port: 443, Protocol: "rest"}, - }, - }, - }, - }, - } - - created, err := sc.Create(ctx, "default", "policy-sb", spec) - require.NoError(t, err) - - // Verify created sandbox has policy - require.NotNil(t, created.Spec.Policy) - assert.Equal(t, uint32(3), created.Spec.Policy.Version) - - // Get it back and verify all fields - got, err := sc.Get(ctx, "default", "policy-sb") - require.NoError(t, err) - require.NotNil(t, got.Spec.Policy) - - p := got.Spec.Policy - assert.Equal(t, uint32(3), p.Version) - - require.NotNil(t, p.Filesystem) - assert.True(t, p.Filesystem.IncludeWorkdir) - assert.Equal(t, []string{"/etc", "/usr/share"}, p.Filesystem.ReadOnly) - assert.Equal(t, []string{"/tmp"}, p.Filesystem.ReadWrite) - - require.NotNil(t, p.Landlock) - assert.Equal(t, "best_effort", p.Landlock.Compatibility) - - require.NotNil(t, p.Process) - assert.Equal(t, "sandbox", p.Process.RunAsUser) - assert.Equal(t, "sandbox-group", p.Process.RunAsGroup) - - require.Len(t, p.NetworkPolicies, 1) - webRule, ok := p.NetworkPolicies["web"] - require.True(t, ok) - assert.Equal(t, "web", webRule.Name) - require.Len(t, webRule.Endpoints, 1) - assert.Equal(t, "api.example.com", webRule.Endpoints[0].Host) - assert.Equal(t, uint32(443), webRule.Endpoints[0].Port) - - // Deep-copy isolation: mutate input spec, verify stored copy unchanged - spec.Policy.Version = 99 - spec.Policy.Filesystem.ReadOnly[0] = "mutated" - spec.Policy.NetworkPolicies["web"] = types.NetworkPolicyRule{Name: "mutated"} - - got2, err := sc.Get(ctx, "default", "policy-sb") - require.NoError(t, err) - assert.Equal(t, uint32(3), got2.Spec.Policy.Version) - assert.Equal(t, "/etc", got2.Spec.Policy.Filesystem.ReadOnly[0]) - assert.Equal(t, "web", got2.Spec.Policy.NetworkPolicies["web"].Name) - - // Deep-copy isolation: mutate returned object, verify store unchanged - got.Spec.Policy.Filesystem.ReadWrite[0] = "mutated" - got3, err := sc.Get(ctx, "default", "policy-sb") - require.NoError(t, err) - assert.Equal(t, "/tmp", got3.Spec.Policy.Filesystem.ReadWrite[0]) -} - -func TestFakeSandboxCreateWithNilPolicy(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - created, err := sc.Create(ctx, "default", "no-policy-sb", &types.SandboxSpec{LogLevel: "info"}) - require.NoError(t, err) - assert.Nil(t, created.Spec.Policy) - - got, err := sc.Get(ctx, "default", "no-policy-sb") - require.NoError(t, err) - assert.Nil(t, got.Spec.Policy) -} - -func TestFakeSandboxCreateFromTemplatePreservesCommandAndTTY(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - sc.templateStore.Insert("default", &types.SandboxWorkloadTemplate{ - Name: "gpu-kata", - ResourceVersion: 7, - Spec: types.SandboxWorkloadTemplateSpec{ - Workload: &types.SandboxWorkloadConfig{ - Image: "registry.example.com/agent:latest", - }, - }, - }) - spec := &types.SandboxSpec{ - Providers: []string{"github"}, - Command: []string{"/opt/worker", "--serve"}, - TTY: true, - } - - created, err := sc.CreateFromTemplate(ctx, "default", "job-1", "gpu-kata", spec, types.WithLabels(map[string]string{"team": "runtime"})) - - require.NoError(t, err) - assert.Equal(t, []string{"/opt/worker", "--serve"}, created.Spec.Command) - assert.True(t, created.Spec.TTY) - assert.Equal(t, []string{"github"}, created.Spec.Providers) - require.NotNil(t, created.CreatedFromWorkloadTemplate) - assert.Equal(t, "gpu-kata", created.CreatedFromWorkloadTemplate.Name) - assert.Equal(t, "7", created.CreatedFromWorkloadTemplate.ResourceVersion) - - spec.Command[0] = "mutated" - got, err := sc.Get(ctx, "default", "job-1") - require.NoError(t, err) - assert.Equal(t, []string{"/opt/worker", "--serve"}, got.Spec.Command) - assert.True(t, got.Spec.TTY) -} - -// --- T032: GetLogs stub tests --- - -func TestSandbox_GetLogs_ReturnsUnimplemented(t *testing.T) { - sc := newTestSandboxClient() - _, err := sc.GetLogs(context.Background(), "default", "sb-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestSandbox_GetLogs_ClosedReturnsUnavailable(t *testing.T) { - store := newobjectStore(sandboxName, copySandbox) - templateStore := newobjectStore(sandboxWorkloadTemplate, copySandboxWorkloadTemplate) - broadcaster := newWatchBroadcaster[*types.Sandbox]() - sc := newFakeSandboxClient(store, templateStore, broadcaster, func() bool { return true }) - _, err := sc.GetLogs(context.Background(), "default", "sb-1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestSandbox_SuspendAndResume(t *testing.T) { - sc := newTestSandboxClient() - ctx := context.Background() - - _, err := sc.Create(ctx, "default", "frozen", &types.SandboxSpec{}) - require.NoError(t, err) - - _, err = sc.Suspend(ctx, "default", "frozen") - require.Error(t, err, "suspend requires Ready") - assert.True(t, types.IsConflict(err)) - - _, err = sc.WaitReady(ctx, "default", "frozen") - require.NoError(t, err) - - suspended, err := sc.Suspend(ctx, "default", "frozen") - require.NoError(t, err) - assert.Equal(t, types.SandboxSuspended, suspended.Status.Phase) - - waited, err := sc.WaitSuspended(ctx, "default", "frozen") - require.NoError(t, err) - assert.Equal(t, types.SandboxSuspended, waited.Status.Phase) - - resumed, err := sc.Resume(ctx, "default", "frozen", types.ResumeOptions{RestartIfRestoreFails: true}) - require.NoError(t, err) - assert.Equal(t, types.SandboxReady, resumed.Status.Phase) - - _, err = sc.Resume(ctx, "default", "frozen") - require.Error(t, err, "resume requires Suspended") -} diff --git a/sdk/go/openshell/v1/fake/service.go b/sdk/go/openshell/v1/fake/service.go deleted file mode 100644 index 3d7a1e0331..0000000000 --- a/sdk/go/openshell/v1/fake/service.go +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// fakeServiceClient implements v1.ServiceInterface. All methods return -// Unimplemented because service exposure requires a real sandbox runtime. -type fakeServiceClient struct { - closedFunc func() bool -} - -// newFakeServiceClient creates a new fakeServiceClient. -func newFakeServiceClient(closedFunc func() bool) *fakeServiceClient { - return &fakeServiceClient{closedFunc: closedFunc} -} - -// Expose returns Unimplemented. -func (c *fakeServiceClient) Expose(_ context.Context, _, _, _ string, _ uint32, _ bool, _ ...v1.ExposeServiceOptions) (*types.ServiceEndpoint, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Expose is not supported by the fake client"} -} - -// Get returns Unimplemented. -func (c *fakeServiceClient) Get(_ context.Context, _, _, _ string) (*types.ServiceEndpoint, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Get is not supported by the fake client"} -} - -// List returns Unimplemented. -func (c *fakeServiceClient) List(_, _ string, _ ...v1.ListOptions) (*v1.Pager[*types.ServiceEndpoint], error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "List is not supported by the fake client"} -} - -// ListAll returns Unimplemented. -func (c *fakeServiceClient) ListAll(_ context.Context, _, _ string, _ ...v1.ListOptions) ([]*types.ServiceEndpoint, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "ListAll is not supported by the fake client"} -} - -// Delete returns Unimplemented. -func (c *fakeServiceClient) Delete(_ context.Context, _, _, _ string, _ ...v1.DeleteOptions) (*types.DeletionResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Delete is not supported by the fake client"} -} - -// Compile-time check that fakeServiceClient implements v1.ServiceInterface. -var _ v1.ServiceInterface = (*fakeServiceClient)(nil) diff --git a/sdk/go/openshell/v1/fake/service_test.go b/sdk/go/openshell/v1/fake/service_test.go deleted file mode 100644 index 052fac33d4..0000000000 --- a/sdk/go/openshell/v1/fake/service_test.go +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- T026: fakeServiceClient stub tests --- - -func TestFakeService_Expose_ReturnsUnimplemented(t *testing.T) { - c := newFakeServiceClient(func() bool { return false }) - _, err := c.Expose(context.Background(), "default", "sb1", "svc1", 8080, false) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeService_Get_ReturnsUnimplemented(t *testing.T) { - c := newFakeServiceClient(func() bool { return false }) - _, err := c.Get(context.Background(), "default", "sb1", "svc1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeService_List_ReturnsUnimplemented(t *testing.T) { - c := newFakeServiceClient(func() bool { return false }) - _, err := c.ListAll(context.Background(), "default", "sb1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeService_Delete_ReturnsUnimplemented(t *testing.T) { - c := newFakeServiceClient(func() bool { return false }) - _, err := c.Delete(context.Background(), "default", "sb1", "svc1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeService_Expose_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeServiceClient(func() bool { return true }) - _, err := c.Expose(context.Background(), "default", "sb1", "svc1", 8080, false) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeService_Get_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeServiceClient(func() bool { return true }) - _, err := c.Get(context.Background(), "default", "sb1", "svc1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeService_List_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeServiceClient(func() bool { return true }) - _, err := c.ListAll(context.Background(), "default", "sb1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeService_Delete_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeServiceClient(func() bool { return true }) - _, err := c.Delete(context.Background(), "default", "sb1", "svc1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} diff --git a/sdk/go/openshell/v1/fake/ssh.go b/sdk/go/openshell/v1/fake/ssh.go deleted file mode 100644 index 950d1f1189..0000000000 --- a/sdk/go/openshell/v1/fake/ssh.go +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "fmt" - "io" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// fakeSSHClient implements v1.SSHInterface. All methods return -// Unimplemented because SSH session management requires a real gateway. -type fakeSSHClient struct { - closedFunc func() bool -} - -// newFakeSSHClient creates a new fakeSSHClient. -func newFakeSSHClient(closedFunc func() bool) *fakeSSHClient { - return &fakeSSHClient{closedFunc: closedFunc} -} - -// CreateSession returns Unimplemented. -func (c *fakeSSHClient) CreateSession(_ context.Context, _, _ string) (*types.SSHSession, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "CreateSession is not supported by the fake client"} -} - -// RevokeSession returns Unimplemented. -func (c *fakeSSHClient) RevokeSession(_ context.Context, _, _ string, _ ...v1.DeleteOptions) (*types.DeletionResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "RevokeSession is not supported by the fake client"} -} - -// Tunnel returns Unimplemented. Ports outside 1-65535 and empty sandbox names -// are rejected with InvalidArgument to match the real client's behavior. -func (c *fakeSSHClient) Tunnel(_ context.Context, _, sandboxName string, port uint32, _ ...v1.TunnelOption) (io.ReadWriteCloser, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if sandboxName == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - if port == 0 || port > 65535 { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: fmt.Sprintf("port must be in range 1-65535, got %d", port)} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Tunnel is not supported by the fake client"} -} - -// Compile-time check that fakeSSHClient implements v1.SSHInterface. -var _ v1.SSHInterface = (*fakeSSHClient)(nil) diff --git a/sdk/go/openshell/v1/fake/ssh_test.go b/sdk/go/openshell/v1/fake/ssh_test.go deleted file mode 100644 index c0859584ef..0000000000 --- a/sdk/go/openshell/v1/fake/ssh_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- T018: fakeSSHClient stub tests --- - -func TestFakeSSH_CreateSession_ReturnsUnimplemented(t *testing.T) { - c := newFakeSSHClient(func() bool { return false }) - _, err := c.CreateSession(context.Background(), "default", "sandbox-1") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeSSH_RevokeSession_ReturnsUnimplemented(t *testing.T) { - c := newFakeSSHClient(func() bool { return false }) - _, err := c.RevokeSession(context.Background(), "default", "tok-abc") - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeSSH_CreateSession_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeSSHClient(func() bool { return true }) - _, err := c.CreateSession(context.Background(), "default", "sandbox-1") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeSSH_RevokeSession_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeSSHClient(func() bool { return true }) - _, err := c.RevokeSession(context.Background(), "default", "tok-abc") - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -// --- T014: Fake Tunnel tests --- - -func TestFakeSSH_Tunnel_ReturnsUnimplemented(t *testing.T) { - c := newFakeSSHClient(func() bool { return false }) - _, err := c.Tunnel(context.Background(), "default", "my-sandbox", 22) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeSSH_Tunnel_WithTunnelOption(t *testing.T) { - c := newFakeSSHClient(func() bool { return false }) - _, err := c.Tunnel(context.Background(), "default", "my-sandbox", 22, v1.WithTunnelServiceID("audit-svc")) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeSSH_Tunnel_InvalidPort(t *testing.T) { - c := newFakeSSHClient(func() bool { return false }) - - _, err := c.Tunnel(context.Background(), "default", "my-sandbox", 0) - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) - - _, err = c.Tunnel(context.Background(), "default", "my-sandbox", 65536) - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestFakeSSH_Tunnel_EmptySandboxName(t *testing.T) { - c := newFakeSSHClient(func() bool { return false }) - _, err := c.Tunnel(context.Background(), "default", "", 22) - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestFakeSSH_Tunnel_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeSSHClient(func() bool { return true }) - _, err := c.Tunnel(context.Background(), "default", "my-sandbox", 22) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} diff --git a/sdk/go/openshell/v1/fake/store.go b/sdk/go/openshell/v1/fake/store.go deleted file mode 100644 index 3d97b240e0..0000000000 --- a/sdk/go/openshell/v1/fake/store.go +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "fmt" - "strings" - "sync" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -func compositeKey(workspace, name string) string { - return workspace + "/" + name -} - -// objectStore is a generic, thread-safe, in-memory store for named objects. -// It deep-copies objects at all boundaries (insert and retrieval) to prevent -// callers from mutating internal state. Items are keyed by composite -// "workspace/name" keys for workspace isolation. -type objectStore[T any] struct { - mu sync.RWMutex - items map[string]T - nameFunc func(T) string - copyFunc func(T) T -} - -// newobjectStore creates a new objectStore with the given name-extraction -// and deep-copy functions. -func newobjectStore[T any](nameFunc func(T) string, copyFunc func(T) T) *objectStore[T] { - return &objectStore[T]{ - items: make(map[string]T), - nameFunc: nameFunc, - copyFunc: copyFunc, - } -} - -// Create adds a new object to the store scoped to the given workspace. -// Returns AlreadyExists if an object with the same workspace/name already -// exists. The object is deep-copied on insert and a deep copy is returned. -func (s *objectStore[T]) Create(workspace string, obj T) (T, error) { - name := s.nameFunc(obj) - key := compositeKey(workspace, name) - s.mu.Lock() - defer s.mu.Unlock() - - if _, exists := s.items[key]; exists { - var zero T - return zero, &types.StatusError{ - Code: types.ErrorAlreadyExists, - Message: fmt.Sprintf("%s already exists", name), - } - } - - stored := s.copyFunc(obj) - s.items[key] = stored - return s.copyFunc(stored), nil -} - -// Get retrieves an object by workspace and name. Returns NotFound if the -// object does not exist. The returned object is a deep copy. -func (s *objectStore[T]) Get(workspace, name string) (T, error) { - key := compositeKey(workspace, name) - s.mu.RLock() - defer s.mu.RUnlock() - - obj, exists := s.items[key] - if !exists { - var zero T - return zero, &types.StatusError{ - Code: types.ErrorNotFound, - Message: fmt.Sprintf("%s not found", name), - } - } - return s.copyFunc(obj), nil -} - -// List returns deep copies of all objects in the given workspace. -func (s *objectStore[T]) List(workspace string) []T { - prefix := workspace + "/" - s.mu.RLock() - defer s.mu.RUnlock() - - result := make([]T, 0) - for key, obj := range s.items { - if strings.HasPrefix(key, prefix) { - result = append(result, s.copyFunc(obj)) - } - } - return result -} - -// ListAll returns deep copies of all objects across all workspaces. -func (s *objectStore[T]) ListAll() []T { - s.mu.RLock() - defer s.mu.RUnlock() - - result := make([]T, 0, len(s.items)) - for _, obj := range s.items { - result = append(result, s.copyFunc(obj)) - } - return result -} - -// Update replaces an existing object in the store within the given workspace. -// Returns NotFound if the object does not exist. The object is deep-copied -// on insert and a deep copy is returned. -func (s *objectStore[T]) Update(workspace string, obj T) (T, error) { - name := s.nameFunc(obj) - key := compositeKey(workspace, name) - s.mu.Lock() - defer s.mu.Unlock() - - if _, exists := s.items[key]; !exists { - var zero T - return zero, &types.StatusError{ - Code: types.ErrorNotFound, - Message: fmt.Sprintf("%s not found", name), - } - } - - stored := s.copyFunc(obj) - s.items[key] = stored - return s.copyFunc(stored), nil -} - -// Delete removes an object from the store by workspace and name. The -// operation is idempotent. -func (s *objectStore[T]) Delete(workspace, name string) { - key := compositeKey(workspace, name) - s.mu.Lock() - defer s.mu.Unlock() - delete(s.items, key) -} - -func (s *objectStore[T]) DeleteWorkspace(workspace string) { - prefix := workspace + "/" - s.mu.Lock() - defer s.mu.Unlock() - for key := range s.items { - if strings.HasPrefix(key, prefix) { - delete(s.items, key) - } - } -} - -// DeleteAndGet atomically removes an object from the store and returns a -// deep copy of the removed object. Returns the zero value and false if the -// object did not exist. -func (s *objectStore[T]) DeleteAndGet(workspace, name string) (T, bool) { - key := compositeKey(workspace, name) - s.mu.Lock() - defer s.mu.Unlock() - - obj, exists := s.items[key] - if !exists { - var zero T - return zero, false - } - delete(s.items, key) - return s.copyFunc(obj), true -} - -// Insert directly places an object into the store without checking for -// duplicates. This is intended for pre-seeding test fixtures. The object -// is deep-copied on insert. -func (s *objectStore[T]) Insert(workspace string, obj T) { - name := s.nameFunc(obj) - key := compositeKey(workspace, name) - s.mu.Lock() - defer s.mu.Unlock() - s.items[key] = s.copyFunc(obj) -} diff --git a/sdk/go/openshell/v1/fake/store_test.go b/sdk/go/openshell/v1/fake/store_test.go deleted file mode 100644 index fff5ad0079..0000000000 --- a/sdk/go/openshell/v1/fake/store_test.go +++ /dev/null @@ -1,293 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "sort" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// testItem is a simple struct used for objectStore tests. -type testItem struct { - Name string - Value string - Tags map[string]string -} - -func testItemName(t *testItem) string { return t.Name } - -func copyTestItem(t *testItem) *testItem { - if t == nil { - return nil - } - c := *t - if t.Tags != nil { - c.Tags = make(map[string]string, len(t.Tags)) - for k, v := range t.Tags { - c.Tags[k] = v - } - } - return &c -} - -func newTestStore() *objectStore[*testItem] { - return newobjectStore(testItemName, copyTestItem) -} - -const testWorkspace = "default" - -func TestObjectStore_Create(t *testing.T) { - s := newTestStore() - - item := &testItem{Name: "alpha", Value: "v1"} - created, err := s.Create(testWorkspace, item) - require.NoError(t, err) - assert.Equal(t, "alpha", created.Name) - assert.Equal(t, "v1", created.Value) -} - -func TestObjectStore_Create_AlreadyExists(t *testing.T) { - s := newTestStore() - - _, err := s.Create(testWorkspace, &testItem{Name: "alpha", Value: "v1"}) - require.NoError(t, err) - - _, err = s.Create(testWorkspace, &testItem{Name: "alpha", Value: "v2"}) - require.Error(t, err) - assert.True(t, types.IsAlreadyExists(err), "expected AlreadyExists error, got: %v", err) -} - -func TestObjectStore_Create_SameNameDifferentWorkspace(t *testing.T) { - s := newTestStore() - - _, err := s.Create("ws-a", &testItem{Name: "alpha", Value: "v1"}) - require.NoError(t, err) - - _, err = s.Create("ws-b", &testItem{Name: "alpha", Value: "v2"}) - require.NoError(t, err) - - gotA, err := s.Get("ws-a", "alpha") - require.NoError(t, err) - assert.Equal(t, "v1", gotA.Value) - - gotB, err := s.Get("ws-b", "alpha") - require.NoError(t, err) - assert.Equal(t, "v2", gotB.Value) -} - -func TestObjectStore_Get(t *testing.T) { - s := newTestStore() - - _, err := s.Create(testWorkspace, &testItem{Name: "alpha", Value: "v1"}) - require.NoError(t, err) - - got, err := s.Get(testWorkspace, "alpha") - require.NoError(t, err) - assert.Equal(t, "alpha", got.Name) - assert.Equal(t, "v1", got.Value) -} - -func TestObjectStore_Get_NotFound(t *testing.T) { - s := newTestStore() - - _, err := s.Get(testWorkspace, "nonexistent") - require.Error(t, err) - assert.True(t, types.IsNotFound(err), "expected NotFound error, got: %v", err) -} - -func TestObjectStore_Get_WrongWorkspace(t *testing.T) { - s := newTestStore() - - _, err := s.Create("ws-a", &testItem{Name: "alpha", Value: "v1"}) - require.NoError(t, err) - - _, err = s.Get("ws-b", "alpha") - require.Error(t, err) - assert.True(t, types.IsNotFound(err), "expected NotFound for wrong workspace") -} - -func TestObjectStore_List_Empty(t *testing.T) { - s := newTestStore() - items := s.List(testWorkspace) - assert.Empty(t, items) -} - -func TestObjectStore_List(t *testing.T) { - s := newTestStore() - - _, _ = s.Create(testWorkspace, &testItem{Name: "alpha", Value: "v1"}) - _, _ = s.Create(testWorkspace, &testItem{Name: "beta", Value: "v2"}) - - items := s.List(testWorkspace) - assert.Len(t, items, 2) - - sort.Slice(items, func(i, j int) bool { return items[i].Name < items[j].Name }) - assert.Equal(t, "alpha", items[0].Name) - assert.Equal(t, "beta", items[1].Name) -} - -func TestObjectStore_List_WorkspaceIsolation(t *testing.T) { - s := newTestStore() - - _, _ = s.Create("ws-a", &testItem{Name: "alpha", Value: "v1"}) - _, _ = s.Create("ws-b", &testItem{Name: "beta", Value: "v2"}) - _, _ = s.Create("ws-a", &testItem{Name: "gamma", Value: "v3"}) - - itemsA := s.List("ws-a") - assert.Len(t, itemsA, 2) - - itemsB := s.List("ws-b") - assert.Len(t, itemsB, 1) - assert.Equal(t, "beta", itemsB[0].Name) -} - -func TestObjectStore_ListAll(t *testing.T) { - s := newTestStore() - - _, _ = s.Create("ws-a", &testItem{Name: "alpha", Value: "v1"}) - _, _ = s.Create("ws-b", &testItem{Name: "beta", Value: "v2"}) - _, _ = s.Create("ws-a", &testItem{Name: "gamma", Value: "v3"}) - - all := s.ListAll() - assert.Len(t, all, 3) -} - -func TestObjectStore_Update(t *testing.T) { - s := newTestStore() - - _, _ = s.Create(testWorkspace, &testItem{Name: "alpha", Value: "v1"}) - - updated, err := s.Update(testWorkspace, &testItem{Name: "alpha", Value: "v2"}) - require.NoError(t, err) - assert.Equal(t, "v2", updated.Value) - - got, err := s.Get(testWorkspace, "alpha") - require.NoError(t, err) - assert.Equal(t, "v2", got.Value) -} - -func TestObjectStore_Update_NotFound(t *testing.T) { - s := newTestStore() - - _, err := s.Update(testWorkspace, &testItem{Name: "nonexistent", Value: "v1"}) - require.Error(t, err) - assert.True(t, types.IsNotFound(err), "expected NotFound error, got: %v", err) -} - -func TestObjectStore_Delete(t *testing.T) { - s := newTestStore() - - _, _ = s.Create(testWorkspace, &testItem{Name: "alpha", Value: "v1"}) - s.Delete(testWorkspace, "alpha") - - _, err := s.Get(testWorkspace, "alpha") - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestObjectStore_Delete_Idempotent(_ *testing.T) { - s := newTestStore() - - s.Delete(testWorkspace, "nonexistent") - - _, _ = s.Create(testWorkspace, &testItem{Name: "alpha", Value: "v1"}) - s.Delete(testWorkspace, "alpha") - s.Delete(testWorkspace, "alpha") -} - -func TestObjectStore_Insert(t *testing.T) { - s := newTestStore() - - s.Insert(testWorkspace, &testItem{Name: "alpha", Value: "v1"}) - - got, err := s.Get(testWorkspace, "alpha") - require.NoError(t, err) - assert.Equal(t, "v1", got.Value) -} - -func TestObjectStore_Insert_Overwrites(t *testing.T) { - s := newTestStore() - - s.Insert(testWorkspace, &testItem{Name: "alpha", Value: "v1"}) - s.Insert(testWorkspace, &testItem{Name: "alpha", Value: "v2"}) - - got, err := s.Get(testWorkspace, "alpha") - require.NoError(t, err) - assert.Equal(t, "v2", got.Value) -} - -func TestObjectStore_DeepCopy_OnCreate(t *testing.T) { - s := newTestStore() - - original := &testItem{Name: "alpha", Value: "v1", Tags: map[string]string{"env": "test"}} - created, err := s.Create(testWorkspace, original) - require.NoError(t, err) - - original.Value = "mutated" - original.Tags["env"] = "mutated" - - got, err := s.Get(testWorkspace, "alpha") - require.NoError(t, err) - assert.Equal(t, "v1", got.Value) - assert.Equal(t, "test", got.Tags["env"]) - - created.Value = "mutated-created" - got2, err := s.Get(testWorkspace, "alpha") - require.NoError(t, err) - assert.Equal(t, "v1", got2.Value) -} - -func TestObjectStore_DeepCopy_OnGet(t *testing.T) { - s := newTestStore() - - _, _ = s.Create(testWorkspace, &testItem{Name: "alpha", Value: "v1", Tags: map[string]string{"env": "test"}}) - - got, err := s.Get(testWorkspace, "alpha") - require.NoError(t, err) - - got.Value = "mutated" - got.Tags["env"] = "mutated" - - got2, err := s.Get(testWorkspace, "alpha") - require.NoError(t, err) - assert.Equal(t, "v1", got2.Value) - assert.Equal(t, "test", got2.Tags["env"]) -} - -func TestObjectStore_DeepCopy_OnList(t *testing.T) { - s := newTestStore() - - _, _ = s.Create(testWorkspace, &testItem{Name: "alpha", Value: "v1", Tags: map[string]string{"env": "test"}}) - - items := s.List(testWorkspace) - require.Len(t, items, 1) - - items[0].Value = "mutated" - items[0].Tags["env"] = "mutated" - - got, err := s.Get(testWorkspace, "alpha") - require.NoError(t, err) - assert.Equal(t, "v1", got.Value) - assert.Equal(t, "test", got.Tags["env"]) -} - -func TestObjectStore_DeepCopy_OnInsert(t *testing.T) { - s := newTestStore() - - original := &testItem{Name: "alpha", Value: "v1", Tags: map[string]string{"env": "test"}} - s.Insert(testWorkspace, original) - - original.Value = "mutated" - original.Tags["env"] = "mutated" - - got, err := s.Get(testWorkspace, "alpha") - require.NoError(t, err) - assert.Equal(t, "v1", got.Value) - assert.Equal(t, "test", got.Tags["env"]) -} diff --git a/sdk/go/openshell/v1/fake/tcp.go b/sdk/go/openshell/v1/fake/tcp.go deleted file mode 100644 index 6a39030bba..0000000000 --- a/sdk/go/openshell/v1/fake/tcp.go +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "fmt" - "io" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// fakeTCPClient implements v1.TCPInterface. All methods return -// Unimplemented because TCP port forwarding requires a real sandbox runtime. -type fakeTCPClient struct { - closedFunc func() bool -} - -// newFakeTCPClient creates a new fakeTCPClient. -func newFakeTCPClient(closedFunc func() bool) *fakeTCPClient { - return &fakeTCPClient{closedFunc: closedFunc} -} - -// Forward returns Unimplemented. Ports outside 1-65535 are rejected with -// InvalidArgument to match the real client's behavior. -func (c *fakeTCPClient) Forward(_ context.Context, _, sandboxName string, port uint32, _ ...v1.ForwardOption) (io.ReadWriteCloser, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if sandboxName == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - if port == 0 || port > 65535 { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: fmt.Sprintf("port must be in range 1-65535, got %d", port)} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Forward is not supported by the fake client"} -} - -// Listen validates inputs then returns Unimplemented. The fake does not bind -// any local port; it checks that sandboxName is non-empty, remotePort is in -// the range 1-65535, and localPort is in the range 0-65535. -func (c *fakeTCPClient) Listen(_ context.Context, _, sandboxName string, remotePort uint32, localPort uint32, _ ...v1.ListenOption) (v1.ForwardListener, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if sandboxName == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - if remotePort == 0 || remotePort > 65535 { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: fmt.Sprintf("port must be in range 1-65535, got %d", remotePort)} - } - if localPort > 65535 { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: fmt.Sprintf("local port must be in range 0-65535, got %d", localPort)} - } - return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Listen is not supported by the fake client"} -} - -// Compile-time check that fakeTCPClient implements v1.TCPInterface. -var _ v1.TCPInterface = (*fakeTCPClient)(nil) diff --git a/sdk/go/openshell/v1/fake/tcp_test.go b/sdk/go/openshell/v1/fake/tcp_test.go deleted file mode 100644 index 5d254b55fd..0000000000 --- a/sdk/go/openshell/v1/fake/tcp_test.go +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- T019: fakeTCPClient stub tests --- - -func TestFakeTCP_Forward_ReturnsUnimplemented(t *testing.T) { - c := newFakeTCPClient(func() bool { return false }) - _, err := c.Forward(context.Background(), "default", "sandbox-1", 8080) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeTCP_Forward_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeTCPClient(func() bool { return true }) - _, err := c.Forward(context.Background(), "default", "sandbox-1", 8080) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeTCP_Forward_WithForwardOption(t *testing.T) { - c := newFakeTCPClient(func() bool { return false }) - _, err := c.Forward(context.Background(), "default", "sandbox-1", 8080, v1.WithForwardServiceID("audit-svc")) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeTCP_Forward_InvalidPort(t *testing.T) { - c := newFakeTCPClient(func() bool { return false }) - _, err := c.Forward(context.Background(), "default", "sandbox-1", 0) - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -// --- T020: fakeTCPClient.Listen tests --- - -func TestFakeTCP_Listen_EmptySandboxName(t *testing.T) { - c := newFakeTCPClient(func() bool { return false }) - ln, err := c.Listen(context.Background(), "default", "", 8080, 0) - assert.Nil(t, ln) - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestFakeTCP_Listen_InvalidRemotePort(t *testing.T) { - c := newFakeTCPClient(func() bool { return false }) - - tests := []struct { - name string - port uint32 - }{ - {"port zero", 0}, - {"port too high", 65536}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ln, err := c.Listen(context.Background(), "default", "my-sandbox", tt.port, 0) - assert.Nil(t, ln) - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) - }) - } -} - -func TestFakeTCP_Listen_ValidInputsReturnUnimplemented(t *testing.T) { - c := newFakeTCPClient(func() bool { return false }) - ln, err := c.Listen(context.Background(), "default", "my-sandbox", 8080, 0) - assert.Nil(t, ln) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} - -func TestFakeTCP_Listen_ClosedReturnsUnavailable(t *testing.T) { - c := newFakeTCPClient(func() bool { return true }) - ln, err := c.Listen(context.Background(), "default", "my-sandbox", 8080, 0) - assert.Nil(t, ln) - require.Error(t, err) - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeTCP_Listen_WithOptions(t *testing.T) { - c := newFakeTCPClient(func() bool { return false }) - ln, err := c.Listen(context.Background(), "default", "my-sandbox", 8080, 0, - v1.WithBindAddress("0.0.0.0"), - v1.WithSSHTunnel(), - v1.WithListenServiceID("svc-1"), - ) - assert.Nil(t, ln) - require.Error(t, err) - assert.True(t, types.IsUnimplemented(err)) -} diff --git a/sdk/go/openshell/v1/fake/workspace.go b/sdk/go/openshell/v1/fake/workspace.go deleted file mode 100644 index 1b5599e977..0000000000 --- a/sdk/go/openshell/v1/fake/workspace.go +++ /dev/null @@ -1,196 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "time" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -func workspaceName(ws *types.Workspace) string { - return ws.Name -} - -func copyWorkspace(ws *types.Workspace) *types.Workspace { - if ws == nil { - return nil - } - cp := *ws - cp.Labels = copyStringMap(ws.Labels) - cp.Annotations = copyStringMap(ws.Annotations) - if ws.DeletionTimestamp != nil { - t := *ws.DeletionTimestamp - cp.DeletionTimestamp = &t - } - return &cp -} - -func memberName(m *types.WorkspaceMember) string { - return m.PrincipalSubject -} - -func copyMember(m *types.WorkspaceMember) *types.WorkspaceMember { - if m == nil { - return nil - } - cp := *m - cp.Labels = copyStringMap(m.Labels) - cp.Annotations = copyStringMap(m.Annotations) - return &cp -} - -type fakeWorkspaceClient struct { - workspaceStore *objectStore[*types.Workspace] - memberStore *objectStore[*types.WorkspaceMember] - closedFunc func() bool -} - -func newFakeWorkspaceClient( - workspaceStore *objectStore[*types.Workspace], - memberStore *objectStore[*types.WorkspaceMember], - closedFunc func() bool, -) *fakeWorkspaceClient { - return &fakeWorkspaceClient{ - workspaceStore: workspaceStore, - memberStore: memberStore, - closedFunc: closedFunc, - } -} - -func (c *fakeWorkspaceClient) Create(_ context.Context, name string, labels map[string]string) (*types.Workspace, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if name == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "workspace name must not be empty"} - } - - ws := &types.Workspace{ - Name: name, - CreatedAt: time.Now(), - Labels: copyStringMap(labels), - ResourceVersion: 1, - Phase: types.WorkspaceActive, - } - - return c.workspaceStore.Create("", ws) -} - -func (c *fakeWorkspaceClient) Get(_ context.Context, name string) (*types.Workspace, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if name == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "workspace name must not be empty"} - } - return c.workspaceStore.Get("", name) -} - -// List returns a lazy pager over workspaces. -func (c *fakeWorkspaceClient) List(opts ...v1.ListOptions) (*v1.Pager[*types.Workspace], error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - var options v1.ListOptions - if len(opts) > 0 { - options = opts[0] - } - return newSlicePager(c.workspaceStore.ListAll(), options.PageSize, options.PageToken) -} - -func (c *fakeWorkspaceClient) ListAll(ctx context.Context, opts ...v1.ListOptions) ([]*types.Workspace, error) { - pager, err := c.List(opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -// Delete removes a workspace. Unlike the sandbox fake (which treats delete as -// idempotent), workspace delete returns NotFound for non-existent workspaces to -// match the gateway's workspace deletion behavior. -func (c *fakeWorkspaceClient) Delete(_ context.Context, name string, opts ...v1.DeleteOptions) (*types.DeletionResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if name == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "workspace name must not be empty"} - } - - _, existed := c.workspaceStore.DeleteAndGet("", name) - if !existed { - return deletionResult(false, "", opts) - } - c.memberStore.DeleteWorkspace(name) - return deletionResult(true, "", opts) -} - -func (c *fakeWorkspaceClient) AddMember(_ context.Context, workspace, principalSubject string, role types.WorkspaceRole) (*types.WorkspaceMember, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if workspace == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "workspace name must not be empty"} - } - if principalSubject == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "principal subject must not be empty"} - } - if role != types.WorkspaceRoleAdmin && role != types.WorkspaceRoleUser { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "role must be Admin or User"} - } - - member := &types.WorkspaceMember{ - Name: principalSubject, - CreatedAt: time.Now(), - ResourceVersion: 1, - PrincipalSubject: principalSubject, - Role: role, - } - - return c.memberStore.Create(workspace, member) -} - -func (c *fakeWorkspaceClient) RemoveMember(_ context.Context, workspace, principalSubject string, opts ...v1.DeleteOptions) (*types.DeletionResult, error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if workspace == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "workspace name must not be empty"} - } - if principalSubject == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "principal subject must not be empty"} - } - - _, existed := c.memberStore.DeleteAndGet(workspace, principalSubject) - if !existed { - return deletionResult(false, "", opts) - } - return deletionResult(true, "", opts) -} - -// ListMembers returns all members for the workspace. ListOptions are accepted for interface compatibility but filtering is not implemented. -func (c *fakeWorkspaceClient) ListMembers(workspace string, opts ...v1.ListOptions) (*v1.Pager[*types.WorkspaceMember], error) { - if c.closedFunc() { - return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"} - } - if workspace == "" { - return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "workspace name must not be empty"} - } - var options v1.ListOptions - if len(opts) > 0 { - options = opts[0] - } - return newSlicePager(c.memberStore.List(workspace), options.PageSize, options.PageToken) -} - -func (c *fakeWorkspaceClient) ListAllMembers(ctx context.Context, workspace string, opts ...v1.ListOptions) ([]*types.WorkspaceMember, error) { - pager, err := c.ListMembers(workspace, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} diff --git a/sdk/go/openshell/v1/fake/workspace_test.go b/sdk/go/openshell/v1/fake/workspace_test.go deleted file mode 100644 index af7e5552c4..0000000000 --- a/sdk/go/openshell/v1/fake/workspace_test.go +++ /dev/null @@ -1,294 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fake - -import ( - "context" - "testing" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestWorkspaceDelete_RemovesMembers(t *testing.T) { - fc := NewClient() - ctx := context.Background() - _, err := fc.Workspaces().Create(ctx, "team", nil) - require.NoError(t, err) - _, err = fc.Workspaces().AddMember(ctx, "team", "alice", types.WorkspaceRoleUser) - require.NoError(t, err) - _, err = fc.Workspaces().Delete(ctx, "team") - require.NoError(t, err) - - members, err := fc.Workspaces().ListAllMembers(ctx, "team") - require.NoError(t, err) - assert.Empty(t, members) -} - -func TestFakeWorkspace_Create(t *testing.T) { - fc := NewClient() - ws, err := fc.Workspaces().Create(context.Background(), "test-ws", map[string]string{"team": "platform"}) - - require.NoError(t, err) - require.NotNil(t, ws) - assert.Equal(t, "test-ws", ws.Name) - assert.Equal(t, map[string]string{"team": "platform"}, ws.Labels) - assert.Equal(t, types.WorkspaceActive, ws.Phase) - assert.Equal(t, uint64(1), ws.ResourceVersion) -} - -func TestFakeWorkspace_Create_EmptyName(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().Create(context.Background(), "", nil) - - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestFakeWorkspace_Create_AlreadyExists(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().Create(context.Background(), "dup-ws", nil) - require.NoError(t, err) - - _, err = fc.Workspaces().Create(context.Background(), "dup-ws", nil) - require.Error(t, err) - assert.True(t, types.IsAlreadyExists(err)) -} - -func TestFakeWorkspace_Create_DeepCopy(t *testing.T) { - fc := NewClient() - labels := map[string]string{"env": "test"} - ws, err := fc.Workspaces().Create(context.Background(), "ws", labels) - require.NoError(t, err) - - labels["env"] = "mutated" - assert.Equal(t, "test", ws.Labels["env"]) - - got, err := fc.Workspaces().Get(context.Background(), "ws") - require.NoError(t, err) - assert.Equal(t, "test", got.Labels["env"]) -} - -func TestFakeWorkspace_Get(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().Create(context.Background(), "my-ws", nil) - require.NoError(t, err) - - ws, err := fc.Workspaces().Get(context.Background(), "my-ws") - require.NoError(t, err) - assert.Equal(t, "my-ws", ws.Name) -} - -func TestFakeWorkspace_Get_EmptyName(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().Get(context.Background(), "") - - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestFakeWorkspace_Get_NotFound(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().Get(context.Background(), "missing") - - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestFakeWorkspace_List(t *testing.T) { - fc := NewClient() - _, _ = fc.Workspaces().Create(context.Background(), "ws-1", nil) - _, _ = fc.Workspaces().Create(context.Background(), "ws-2", nil) - - workspaces, err := fc.Workspaces().ListAll(context.Background()) - require.NoError(t, err) - assert.Len(t, workspaces, 2) -} - -func TestFakeWorkspace_List_Empty(t *testing.T) { - fc := NewClient() - workspaces, err := fc.Workspaces().ListAll(context.Background()) - require.NoError(t, err) - assert.Empty(t, workspaces) -} - -func TestFakeWorkspace_Delete(t *testing.T) { - fc := NewClient() - _, _ = fc.Workspaces().Create(context.Background(), "del-ws", nil) - - _, err := fc.Workspaces().Delete(context.Background(), "del-ws") - require.NoError(t, err) - - _, err = fc.Workspaces().Get(context.Background(), "del-ws") - assert.True(t, types.IsNotFound(err)) -} - -func TestFakeWorkspace_Delete_EmptyName(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().Delete(context.Background(), "") - - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestFakeWorkspace_Delete_NotFound(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().Delete(context.Background(), "missing") - - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestFakeWorkspace_AddMember(t *testing.T) { - fc := NewClient() - m, err := fc.Workspaces().AddMember(context.Background(), "ws", "user@example.com", types.WorkspaceRoleAdmin) - - require.NoError(t, err) - require.NotNil(t, m) - assert.Equal(t, "user@example.com", m.PrincipalSubject) - assert.Equal(t, types.WorkspaceRoleAdmin, m.Role) -} - -func TestFakeWorkspace_AddMember_EmptyWorkspace(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().AddMember(context.Background(), "", "user@example.com", types.WorkspaceRoleAdmin) - - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestFakeWorkspace_AddMember_EmptySubject(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().AddMember(context.Background(), "ws", "", types.WorkspaceRoleAdmin) - - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestFakeWorkspace_AddMember_InvalidRole(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().AddMember(context.Background(), "ws", "user@example.com", types.WorkspaceRole("invalid")) - - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestFakeWorkspace_AddMember_AlreadyExists(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().AddMember(context.Background(), "ws", "user@example.com", types.WorkspaceRoleAdmin) - require.NoError(t, err) - - _, err = fc.Workspaces().AddMember(context.Background(), "ws", "user@example.com", types.WorkspaceRoleUser) - require.Error(t, err) - assert.True(t, types.IsAlreadyExists(err)) -} - -func TestFakeWorkspace_RemoveMember(t *testing.T) { - fc := NewClient() - _, _ = fc.Workspaces().AddMember(context.Background(), "ws", "user@example.com", types.WorkspaceRoleAdmin) - - _, err := fc.Workspaces().RemoveMember(context.Background(), "ws", "user@example.com") - require.NoError(t, err) - - members, err := fc.Workspaces().ListAllMembers(context.Background(), "ws") - require.NoError(t, err) - assert.Empty(t, members) -} - -func TestFakeWorkspace_RemoveMember_EmptyWorkspace(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().RemoveMember(context.Background(), "", "user@example.com") - - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestFakeWorkspace_RemoveMember_EmptySubject(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().RemoveMember(context.Background(), "ws", "") - - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestFakeWorkspace_RemoveMember_NotFound(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().RemoveMember(context.Background(), "ws", "missing@example.com") - - require.Error(t, err) - assert.True(t, types.IsNotFound(err)) -} - -func TestFakeWorkspace_ListMembers(t *testing.T) { - fc := NewClient() - _, _ = fc.Workspaces().AddMember(context.Background(), "ws", "user1@example.com", types.WorkspaceRoleAdmin) - _, _ = fc.Workspaces().AddMember(context.Background(), "ws", "user2@example.com", types.WorkspaceRoleUser) - - members, err := fc.Workspaces().ListAllMembers(context.Background(), "ws") - require.NoError(t, err) - assert.Len(t, members, 2) -} - -func TestFakeWorkspace_ListMembers_EmptyWorkspace(t *testing.T) { - fc := NewClient() - _, err := fc.Workspaces().ListAllMembers(context.Background(), "") - - require.Error(t, err) - assert.True(t, types.IsInvalidArgument(err)) -} - -func TestFakeWorkspace_ListMembers_Isolation(t *testing.T) { - fc := NewClient() - _, _ = fc.Workspaces().AddMember(context.Background(), "ws-a", "user@example.com", types.WorkspaceRoleAdmin) - _, _ = fc.Workspaces().AddMember(context.Background(), "ws-b", "other@example.com", types.WorkspaceRoleUser) - - membersA, err := fc.Workspaces().ListAllMembers(context.Background(), "ws-a") - require.NoError(t, err) - assert.Len(t, membersA, 1) - assert.Equal(t, "user@example.com", membersA[0].PrincipalSubject) - - membersB, err := fc.Workspaces().ListAllMembers(context.Background(), "ws-b") - require.NoError(t, err) - assert.Len(t, membersB, 1) - assert.Equal(t, "other@example.com", membersB[0].PrincipalSubject) -} - -func TestFakeWorkspace_Closed(t *testing.T) { - fc := NewClient() - _ = fc.Close() - - _, err := fc.Workspaces().Create(context.Background(), "ws", nil) - assert.True(t, types.IsUnavailable(err)) - - _, err = fc.Workspaces().Get(context.Background(), "ws") - assert.True(t, types.IsUnavailable(err)) - - _, err = fc.Workspaces().ListAll(context.Background()) - assert.True(t, types.IsUnavailable(err)) - - _, err = fc.Workspaces().Delete(context.Background(), "ws") - assert.True(t, types.IsUnavailable(err)) - - _, err = fc.Workspaces().AddMember(context.Background(), "ws", "user", types.WorkspaceRoleAdmin) - assert.True(t, types.IsUnavailable(err)) - - _, err = fc.Workspaces().RemoveMember(context.Background(), "ws", "user") - assert.True(t, types.IsUnavailable(err)) - - _, err = fc.Workspaces().ListAllMembers(context.Background(), "ws") - assert.True(t, types.IsUnavailable(err)) -} - -func TestFakeWorkspace_AddWorkspace(t *testing.T) { - fc := NewClient() - fc.AddWorkspace(&types.Workspace{ - Name: "preseeded", - Phase: types.WorkspaceActive, - }) - - ws, err := fc.Workspaces().Get(context.Background(), "preseeded") - require.NoError(t, err) - assert.Equal(t, "preseeded", ws.Name) -} diff --git a/sdk/go/openshell/v1/file.go b/sdk/go/openshell/v1/file.go deleted file mode 100644 index 31a9a2e413..0000000000 --- a/sdk/go/openshell/v1/file.go +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "errors" -) - -// ErrTransportNotAvailable indicates that this SDK build has no file-transfer -// transport. Callers can detect it with errors.Is. -var ErrTransportNotAvailable = errors.New("openshell: file transport not available") - -// FileInterface defines file transfer operations on sandboxes. -// Methods accept a sandbox name and resolve it to an ID internally. -type FileInterface interface { - Upload(ctx context.Context, workspace, sandboxName string, localPath string, remotePath string) error - Download(ctx context.Context, workspace, sandboxName string, remotePath string, localPath string) error -} diff --git a/sdk/go/openshell/v1/file_client.go b/sdk/go/openshell/v1/file_client.go deleted file mode 100644 index dd57410630..0000000000 --- a/sdk/go/openshell/v1/file_client.go +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "fmt" - "os" - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" -) - -type fileClient struct { - client pb.OpenShellClient - sandboxes SandboxInterface - transport sshTransport -} - -type sshTransport interface { - available() bool - upload(ctx context.Context, session *pb.CreateSshSessionResponse, localPath, remotePath string) error - download(ctx context.Context, session *pb.CreateSshSessionResponse, remotePath, localPath string) error -} - -func newFileClient(conn grpc.ClientConnInterface, sandboxes SandboxInterface) *fileClient { - return &fileClient{ - client: pb.NewOpenShellClient(conn), - sandboxes: sandboxes, - transport: &defaultSSHTransport{}, - } -} - -func (f *fileClient) Upload(ctx context.Context, workspace, sandboxName string, localPath string, remotePath string) error { - if !f.transport.available() { - return fmt.Errorf("upload: %w", ErrTransportNotAvailable) - } - if sandboxName == "" { - return &StatusError{Code: ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - if remotePath == "" { - return &StatusError{Code: ErrorInvalidArgument, Message: "remote path must not be empty"} - } - if _, err := f.sandboxes.Get(ctx, workspace, sandboxName); err != nil { - return err - } - - info, err := os.Stat(localPath) - if err != nil { - return fmt.Errorf("local file error: %w", err) - } - if info.IsDir() { - return fmt.Errorf("local path is a directory, not a file: %s", localPath) - } - - session, err := f.client.CreateSshSession(ctx, &pb.CreateSshSessionRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return converter.FromGRPCError(err) - } - - defer func() { - revokeCtx, revokeCancel := context.WithTimeout(context.Background(), 5*time.Second) - defer revokeCancel() - _, _ = f.client.RevokeSshSession(revokeCtx, &pb.RevokeSshSessionRequest{ - Token: session.GetToken(), - }) - }() - - return f.transport.upload(ctx, session, localPath, remotePath) -} - -func (f *fileClient) Download(ctx context.Context, workspace, sandboxName string, remotePath string, localPath string) error { - if !f.transport.available() { - return fmt.Errorf("download: %w", ErrTransportNotAvailable) - } - if sandboxName == "" { - return &StatusError{Code: ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - if remotePath == "" { - return &StatusError{Code: ErrorInvalidArgument, Message: "remote path must not be empty"} - } - if _, err := f.sandboxes.Get(ctx, workspace, sandboxName); err != nil { - return err - } - - session, err := f.client.CreateSshSession(ctx, &pb.CreateSshSessionRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return converter.FromGRPCError(err) - } - - defer func() { - revokeCtx, revokeCancel := context.WithTimeout(context.Background(), 5*time.Second) - defer revokeCancel() - _, _ = f.client.RevokeSshSession(revokeCtx, &pb.RevokeSshSessionRequest{ - Token: session.GetToken(), - }) - }() - - return f.transport.download(ctx, session, remotePath, localPath) -} - -type defaultSSHTransport struct{} - -func (t *defaultSSHTransport) available() bool { return false } - -func (t *defaultSSHTransport) upload(_ context.Context, session *pb.CreateSshSessionResponse, localPath, remotePath string) error { - return fmt.Errorf("SSH transport to %s:%d not implemented (local: %s -> remote: %s)", - session.GetGatewayHost(), session.GetGatewayPort(), localPath, remotePath) -} - -func (t *defaultSSHTransport) download(_ context.Context, session *pb.CreateSshSessionResponse, remotePath, localPath string) error { - return fmt.Errorf("SSH transport to %s:%d not implemented (remote: %s -> local: %s)", - session.GetGatewayHost(), session.GetGatewayPort(), remotePath, localPath) -} diff --git a/sdk/go/openshell/v1/file_client_test.go b/sdk/go/openshell/v1/file_client_test.go deleted file mode 100644 index 0ce3cb0116..0000000000 --- a/sdk/go/openshell/v1/file_client_test.go +++ /dev/null @@ -1,343 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "errors" - "net" - "os" - "path/filepath" - "testing" - - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" -) - -type availableSSHTransport struct{} - -func (availableSSHTransport) available() bool { return true } -func (availableSSHTransport) upload(context.Context, *pb.CreateSshSessionResponse, string, string) error { - return nil -} -func (availableSSHTransport) download(context.Context, *pb.CreateSshSessionResponse, string, string) error { - return nil -} - -type mockFileServer struct { - pb.UnimplementedOpenShellServer - createResp *pb.CreateSshSessionResponse - createErr error - revokeResp *pb.RevokeSshSessionResponse - revokeErr error - lastCreateReq *pb.CreateSshSessionRequest - lastRevokeReq *pb.RevokeSshSessionRequest - createCallCount int - revokeCallCount int -} - -func newMockFileServer() *mockFileServer { - return &mockFileServer{} -} - -func (s *mockFileServer) CreateSshSession(_ context.Context, req *pb.CreateSshSessionRequest) (*pb.CreateSshSessionResponse, error) { //nolint:revive // method name matches proto interface - s.lastCreateReq = req - s.createCallCount++ - if s.createErr != nil { - return nil, s.createErr - } - return s.createResp, nil -} - -func (s *mockFileServer) RevokeSshSession(_ context.Context, req *pb.RevokeSshSessionRequest) (*pb.RevokeSshSessionResponse, error) { //nolint:revive // method name matches proto interface - s.lastRevokeReq = req - s.revokeCallCount++ - if s.revokeErr != nil { - return nil, s.revokeErr - } - return s.revokeResp, nil -} - -func setupFileTest(t *testing.T, mock *mockFileServer) (*fileClient, func()) { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - - client := newFileClient(conn, &stubSandboxResolver{}) - client.transport = availableSSHTransport{} - return client, func() { - _ = conn.Close() - srv.Stop() - } -} - -// --- T051: Upload and Download tests --- - -func TestFileUpload(t *testing.T) { - mock := newMockFileServer() - mock.createResp = &pb.CreateSshSessionResponse{ - SandboxId: "sb-test-sandbox", - Token: "session-token-123", - GatewayHost: "gateway.example.com", - GatewayPort: 2222, - } - mock.revokeResp = &pb.RevokeSshSessionResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_COMPLETED} - client, cleanup := setupFileTest(t, mock) - defer cleanup() - - tmpDir := t.TempDir() - localPath := filepath.Join(tmpDir, "upload.txt") - require.NoError(t, os.WriteFile(localPath, []byte("file content"), 0644)) - - err := client.Upload(context.Background(), "default", "test-sandbox", localPath, "/remote/upload.txt") - - require.NoError(t, err) - assert.Equal(t, "test-sandbox", mock.lastCreateReq.GetSandbox()) - assert.Equal(t, 1, mock.createCallCount) -} - -func TestFileTransfer_DefaultTransportReturnsUnavailableBeforeRPC(t *testing.T) { - mock := newMockFileServer() - client, cleanup := setupFileTest(t, mock) - defer cleanup() - client.transport = &defaultSSHTransport{} - - err := client.Upload(context.Background(), "default", "sandbox", "/missing/local/file", "/remote/file") - require.Error(t, err) - assert.True(t, errors.Is(err, ErrTransportNotAvailable)) - assert.Equal(t, 0, mock.createCallCount) - - err = client.Download(context.Background(), "default", "sandbox", "/remote/file", "/local/file") - require.Error(t, err) - assert.True(t, errors.Is(err, ErrTransportNotAvailable)) - assert.Equal(t, 0, mock.createCallCount) -} - -func TestFileUpload_CreateSessionError(t *testing.T) { - mock := newMockFileServer() - mock.createErr = status.Error(codes.NotFound, "sandbox not found") - client, cleanup := setupFileTest(t, mock) - defer cleanup() - - tmpDir := t.TempDir() - localPath := filepath.Join(tmpDir, "upload.txt") - require.NoError(t, os.WriteFile(localPath, []byte("content"), 0644)) - - err := client.Upload(context.Background(), "default", "test-sandbox", localPath, "/remote/file.txt") - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestFileDownload(t *testing.T) { - mock := newMockFileServer() - mock.createResp = &pb.CreateSshSessionResponse{ - SandboxId: "sb-test-sandbox", - Token: "session-token-456", - GatewayHost: "gateway.example.com", - GatewayPort: 2222, - } - mock.revokeResp = &pb.RevokeSshSessionResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_COMPLETED} - client, cleanup := setupFileTest(t, mock) - defer cleanup() - - tmpDir := t.TempDir() - localPath := filepath.Join(tmpDir, "download.txt") - - err := client.Download(context.Background(), "default", "test-sandbox", "/remote/file.txt", localPath) - - require.NoError(t, err) - assert.Equal(t, "test-sandbox", mock.lastCreateReq.GetSandbox()) - assert.Equal(t, 1, mock.createCallCount) -} - -func TestFileDownload_CreateSessionError(t *testing.T) { - mock := newMockFileServer() - mock.createErr = status.Error(codes.PermissionDenied, "access denied") - client, cleanup := setupFileTest(t, mock) - defer cleanup() - - tmpDir := t.TempDir() - localPath := filepath.Join(tmpDir, "download.txt") - - err := client.Download(context.Background(), "default", "test-sandbox", "/remote/file.txt", localPath) - - require.Error(t, err) - assert.True(t, IsPermissionDenied(err)) -} - -// --- T052: Upload error cases --- - -func TestFileUpload_NonExistentLocalFile(t *testing.T) { - mock := newMockFileServer() - client, cleanup := setupFileTest(t, mock) - defer cleanup() - - err := client.Upload(context.Background(), "default", "test-sandbox", "/nonexistent/file.txt", "/remote/file.txt") - - require.Error(t, err) - // Should fail before contacting gateway - assert.Equal(t, 0, mock.createCallCount) -} - -func TestFileUpload_LocalPathIsDirectory(t *testing.T) { - mock := newMockFileServer() - client, cleanup := setupFileTest(t, mock) - defer cleanup() - - tmpDir := t.TempDir() - - err := client.Upload(context.Background(), "default", "test-sandbox", tmpDir, "/remote/file.txt") - - require.Error(t, err) - // Should fail before contacting gateway - assert.Equal(t, 0, mock.createCallCount) -} - -func TestFileUpload_EmptySandboxName(t *testing.T) { - mock := newMockFileServer() - client, cleanup := setupFileTest(t, mock) - defer cleanup() - - tmpDir := t.TempDir() - localPath := filepath.Join(tmpDir, "file.txt") - require.NoError(t, os.WriteFile(localPath, []byte("content"), 0644)) - - err := client.Upload(context.Background(), "default", "", localPath, "/remote/file.txt") - - require.Error(t, err) - assert.Equal(t, 0, mock.createCallCount) -} - -func TestFileDownload_EmptyRemotePath(t *testing.T) { - mock := newMockFileServer() - client, cleanup := setupFileTest(t, mock) - defer cleanup() - - tmpDir := t.TempDir() - localPath := filepath.Join(tmpDir, "download.txt") - - err := client.Download(context.Background(), "default", "test-sandbox", "", localPath) - - require.Error(t, err) - assert.Equal(t, 0, mock.createCallCount) -} - -// --- Name-to-ID resolution tests --- - -func TestFileUpload_UsesName(t *testing.T) { - mock := newMockFileServer() - mock.createResp = &pb.CreateSshSessionResponse{ - SandboxId: "sb-my-sandbox", - Token: "token", - GatewayHost: "gw.example.com", - GatewayPort: 2222, - } - mock.revokeResp = &pb.RevokeSshSessionResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_COMPLETED} - client, cleanup := setupFileTest(t, mock) - defer cleanup() - - tmpDir := t.TempDir() - localPath := filepath.Join(tmpDir, "upload.txt") - require.NoError(t, os.WriteFile(localPath, []byte("content"), 0644)) - - _ = client.Upload(context.Background(), "default", "my-sandbox", localPath, "/remote/file.txt") - - // Verify the proto request contains the resolved ID, not the name - assert.Equal(t, "my-sandbox", mock.lastCreateReq.GetSandbox()) -} - -func TestFileUpload_ResolutionError(t *testing.T) { - mock := newMockFileServer() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - defer func() { - _ = conn.Close() - srv.Stop() - }() - - resolver := &stubSandboxResolver{ - getErr: &StatusError{Code: ErrorNotFound, Message: "sandbox not found"}, - } - client := newFileClient(conn, resolver) - client.transport = availableSSHTransport{} - - tmpDir := t.TempDir() - localPath := filepath.Join(tmpDir, "upload.txt") - require.NoError(t, os.WriteFile(localPath, []byte("content"), 0644)) - - err = client.Upload(context.Background(), "default", "nonexistent", localPath, "/remote/file.txt") - require.Error(t, err) - assert.True(t, IsNotFound(err)) - assert.Equal(t, 0, mock.createCallCount) -} - -func TestFileDownload_UsesName(t *testing.T) { - mock := newMockFileServer() - client, cleanup := setupFileTest(t, mock) - defer cleanup() - - localPath := filepath.Join(t.TempDir(), "downloaded.txt") - _ = client.Download(context.Background(), "default", "my-sandbox", "/remote/file.txt", localPath) - - assert.Equal(t, "my-sandbox", mock.lastCreateReq.GetSandbox()) -} - -func TestFileDownload_ResolutionError(t *testing.T) { - mock := newMockFileServer() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - defer func() { - _ = conn.Close() - srv.Stop() - }() - - resolver := &stubSandboxResolver{ - getErr: &StatusError{Code: ErrorNotFound, Message: "sandbox not found"}, - } - client := newFileClient(conn, resolver) - client.transport = availableSSHTransport{} - - localPath := filepath.Join(t.TempDir(), "downloaded.txt") - err = client.Download(context.Background(), "default", "nonexistent", "/remote/file.txt", localPath) - require.Error(t, err) - assert.True(t, IsNotFound(err)) - assert.Equal(t, 0, mock.createCallCount) -} diff --git a/sdk/go/openshell/v1/gateway/config.go b/sdk/go/openshell/v1/gateway/config.go deleted file mode 100644 index c35581653b..0000000000 --- a/sdk/go/openshell/v1/gateway/config.go +++ /dev/null @@ -1,167 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package gateway - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" -) - -// AuthMode represents the authentication mode configured for a gateway. -type AuthMode string - -// Known auth mode values matching the Rust CLI's gateway configuration. -const ( - // AuthModeNone indicates no authentication (default when auth_mode is - // unset or explicitly "none"). - AuthModeNone AuthMode = "" - - // AuthModePlaintext indicates an insecure plaintext connection with - // no TLS and no authentication. - AuthModePlaintext AuthMode = "plaintext" - - // AuthModeCloudflareJWT indicates Cloudflare Access JWT authentication - // using an edge token loaded from disk. - AuthModeCloudflareJWT AuthMode = "cloudflare_jwt" - - // AuthModeOIDC indicates OpenID Connect authentication using a - // refreshable token bundle loaded from disk. - AuthModeOIDC AuthMode = "oidc" - - // AuthModeMTLS indicates mutual TLS authentication. Currently - // unsupported; returns [ErrUnsupportedAuthMode] with guidance. - AuthModeMTLS AuthMode = "mtls" -) - -// ConfigSource identifies where a gateway configuration was found. -type ConfigSource string - -const ( - // SourceUser indicates the gateway was found in the user config - // directory ($XDG_CONFIG_HOME/openshell/gateways/). - SourceUser ConfigSource = "user" - - // SourceSystem indicates the gateway was found in the system config - // directory (/etc/openshell/gateways/). - SourceSystem ConfigSource = "system" -) - -// Config is a parsed representation of a gateway's on-disk metadata.json. -// It is an immutable snapshot captured at load time; subsequent changes to -// the on-disk files are not reflected. -type Config struct { - // Name is the validated gateway name. - Name string - - // Endpoint is the host:port address of the gateway. - Endpoint string - - // AuthMode is the resolved authentication mode. - AuthMode AuthMode - - // Source indicates whether the config came from the user or system - // directory. - Source ConfigSource - - // Dir is the absolute path to the gateway config directory. - Dir string - - // OIDCIssuer is the OIDC provider's issuer URL read from - // metadata.json. Empty when the gateway does not use OIDC auth. - OIDCIssuer string - - // OIDCClientID is the OAuth2 client ID read from metadata.json. - // Empty when the gateway does not use OIDC auth. - OIDCClientID string - - // OIDCAudience is the optional resource-server audience. - OIDCAudience string - - // OIDCScopes contains the configured space-separated OAuth2 scopes. - OIDCScopes string -} - -// Info is a lightweight summary of a gateway for listing purposes. -// It does not load tokens or validate config completeness. -type Info struct { - // Name is the gateway name derived from the directory listing. - Name string - - // Active indicates whether this is the currently active gateway. - Active bool - - // Source indicates whether the gateway is from the user or system - // directory. - Source ConfigSource -} - -// metadataJSON is the on-disk representation of metadata.json. -// Unknown fields are silently ignored for forward compatibility. -type metadataJSON struct { - Endpoint string `json:"gateway_endpoint"` - AuthMode string `json:"auth_mode"` - Name string `json:"name"` - OIDCIssuer string `json:"oidc_issuer"` - OIDCClientID string `json:"oidc_client_id"` - OIDCAudience string `json:"oidc_audience"` - OIDCScopes string `json:"oidc_scopes"` -} - -// parseAuthMode converts a raw auth_mode string to the typed AuthMode. -// Empty string and "none" both map to AuthModeNone. -func parseAuthMode(raw string) (AuthMode, error) { - switch raw { - case "", "none": - return AuthModeNone, nil - case "plaintext": - return AuthModePlaintext, nil - case "cloudflare_jwt": - return AuthModeCloudflareJWT, nil - case "oidc": - return AuthModeOIDC, nil - case "mtls": - return AuthModeMTLS, nil - default: - return "", fmt.Errorf("%w: %q", ErrUnsupportedAuthMode, raw) - } -} - -// parseMetadata reads and parses metadata.json from the given gateway -// directory. Unknown fields are silently ignored for forward compatibility -// with newer Rust CLI versions. -func parseMetadata(dir string) (*Config, error) { - path := filepath.Join(dir, "metadata.json") - - data, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("%w: %v", ErrConfigParse, err) - } - - var meta metadataJSON - if err := json.Unmarshal(data, &meta); err != nil { - return nil, fmt.Errorf("%w: invalid JSON in %s: %v", ErrConfigParse, path, err) - } - - if meta.Endpoint == "" { - return nil, fmt.Errorf("%w: missing gateway_endpoint in %s", ErrConfigParse, path) - } - - mode, err := parseAuthMode(meta.AuthMode) - if err != nil { - return nil, err - } - - return &Config{ - Name: meta.Name, - Endpoint: meta.Endpoint, - AuthMode: mode, - Dir: dir, - OIDCIssuer: meta.OIDCIssuer, - OIDCClientID: meta.OIDCClientID, - OIDCAudience: meta.OIDCAudience, - OIDCScopes: meta.OIDCScopes, - }, nil -} diff --git a/sdk/go/openshell/v1/gateway/config_test.go b/sdk/go/openshell/v1/gateway/config_test.go deleted file mode 100644 index 8ec604d2fc..0000000000 --- a/sdk/go/openshell/v1/gateway/config_test.go +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package gateway - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// --- T010: metadata.json parsing tests --- - -func TestParseMetadata_ValidConfig(t *testing.T) { - dir := t.TempDir() - writeJSON(t, dir, `{"gateway_endpoint":"localhost:8080","auth_mode":"none","name":"prod"}`) - - cfg, err := parseMetadata(dir) - require.NoError(t, err) - assert.Equal(t, "prod", cfg.Name) - assert.Equal(t, "localhost:8080", cfg.Endpoint) - assert.Equal(t, AuthModeNone, cfg.AuthMode) - assert.Equal(t, dir, cfg.Dir) -} - -func TestParseMetadata_EmptyAuthMode(t *testing.T) { - dir := t.TempDir() - writeJSON(t, dir, `{"gateway_endpoint":"host:443"}`) - - cfg, err := parseMetadata(dir) - require.NoError(t, err) - assert.Equal(t, AuthModeNone, cfg.AuthMode) -} - -func TestParseMetadata_AllAuthModes(t *testing.T) { - cases := []struct { - mode string - expected AuthMode - }{ - {"", AuthModeNone}, - {"none", AuthModeNone}, - {"plaintext", AuthModePlaintext}, - {"cloudflare_jwt", AuthModeCloudflareJWT}, - {"oidc", AuthModeOIDC}, - {"mtls", AuthModeMTLS}, - } - - for _, tc := range cases { - t.Run("mode_"+tc.mode, func(t *testing.T) { - dir := t.TempDir() - if tc.mode == "" { - writeJSON(t, dir, `{"gateway_endpoint":"host:443"}`) - } else { - writeJSON(t, dir, `{"gateway_endpoint":"host:443","auth_mode":"`+tc.mode+`"}`) - } - - cfg, err := parseMetadata(dir) - require.NoError(t, err) - assert.Equal(t, tc.expected, cfg.AuthMode) - }) - } -} - -func TestParseMetadata_MissingEndpoint(t *testing.T) { - dir := t.TempDir() - writeJSON(t, dir, `{"auth_mode":"none","name":"prod"}`) - - _, err := parseMetadata(dir) - require.Error(t, err) - assert.ErrorIs(t, err, ErrConfigParse) - assert.Contains(t, err.Error(), "missing gateway_endpoint") -} - -func TestParseMetadata_MissingFile(t *testing.T) { - dir := t.TempDir() - - _, err := parseMetadata(dir) - require.Error(t, err) - assert.ErrorIs(t, err, ErrConfigParse) -} - -func TestParseMetadata_MalformedJSON(t *testing.T) { - dir := t.TempDir() - writeJSON(t, dir, `{invalid json}`) - - _, err := parseMetadata(dir) - require.Error(t, err) - assert.ErrorIs(t, err, ErrConfigParse) - assert.Contains(t, err.Error(), "invalid JSON") -} - -func TestParseMetadata_UnknownFieldsIgnored(t *testing.T) { - dir := t.TempDir() - writeJSON(t, dir, `{ - "gateway_endpoint":"host:443", - "auth_mode":"none", - "name":"prod", - "future_field":"some_value", - "another_new_thing": 42 - }`) - - cfg, err := parseMetadata(dir) - require.NoError(t, err) - assert.Equal(t, "prod", cfg.Name) - assert.Equal(t, "host:443", cfg.Endpoint) -} - -func TestParseMetadata_UnsupportedAuthMode(t *testing.T) { - dir := t.TempDir() - writeJSON(t, dir, `{"gateway_endpoint":"host:443","auth_mode":"kerberos"}`) - - _, err := parseMetadata(dir) - require.Error(t, err) - assert.ErrorIs(t, err, ErrUnsupportedAuthMode) - assert.Contains(t, err.Error(), "kerberos") -} - -func TestParseAuthMode(t *testing.T) { - cases := []struct { - input string - expected AuthMode - wantErr bool - }{ - {"", AuthModeNone, false}, - {"none", AuthModeNone, false}, - {"plaintext", AuthModePlaintext, false}, - {"cloudflare_jwt", AuthModeCloudflareJWT, false}, - {"oidc", AuthModeOIDC, false}, - {"mtls", AuthModeMTLS, false}, - {"unknown", "", true}, - {"NONE", "", true}, // case-sensitive - } - - for _, tc := range cases { - t.Run("input_"+tc.input, func(t *testing.T) { - mode, err := parseAuthMode(tc.input) - if tc.wantErr { - require.Error(t, err) - assert.ErrorIs(t, err, ErrUnsupportedAuthMode) - } else { - require.NoError(t, err) - assert.Equal(t, tc.expected, mode) - } - }) - } -} - -// --- T010: OIDC config field tests --- - -func TestParseMetadata_OIDCFields(t *testing.T) { - dir := t.TempDir() - writeJSON(t, dir, `{ - "gateway_endpoint":"host:443", - "auth_mode":"oidc", - "name":"oidc-gw", - "oidc_issuer":"https://auth.example.com", - "oidc_client_id":"my-client-id" - ,"oidc_audience":"openshell-api", - "oidc_scopes":"sandbox:read sandbox:write" - }`) - - cfg, err := parseMetadata(dir) - require.NoError(t, err) - assert.Equal(t, "oidc-gw", cfg.Name) - assert.Equal(t, AuthModeOIDC, cfg.AuthMode) - assert.Equal(t, "https://auth.example.com", cfg.OIDCIssuer) - assert.Equal(t, "my-client-id", cfg.OIDCClientID) - assert.Equal(t, "openshell-api", cfg.OIDCAudience) - assert.Equal(t, "sandbox:read sandbox:write", cfg.OIDCScopes) -} - -func TestParseMetadata_OIDCFieldsMissing(t *testing.T) { - // When OIDC fields are absent (older gateway or non-OIDC mode), - // the Config should have empty strings for OIDCIssuer/OIDCClientID. - dir := t.TempDir() - writeJSON(t, dir, `{ - "gateway_endpoint":"host:443", - "auth_mode":"cloudflare_jwt", - "name":"legacy-gw" - }`) - - cfg, err := parseMetadata(dir) - require.NoError(t, err) - assert.Equal(t, "", cfg.OIDCIssuer) - assert.Equal(t, "", cfg.OIDCClientID) -} - -func TestParseMetadata_OIDCFieldsEmpty(t *testing.T) { - // Explicit empty strings for OIDC fields should be handled - // gracefully (backward compatibility). - dir := t.TempDir() - writeJSON(t, dir, `{ - "gateway_endpoint":"host:443", - "auth_mode":"oidc", - "oidc_issuer":"", - "oidc_client_id":"" - }`) - - cfg, err := parseMetadata(dir) - require.NoError(t, err) - assert.Equal(t, "", cfg.OIDCIssuer) - assert.Equal(t, "", cfg.OIDCClientID) -} - -// writeJSON is a test helper that writes a metadata.json file. -func writeJSON(t *testing.T, dir, content string) { - t.Helper() - err := os.WriteFile(filepath.Join(dir, "metadata.json"), []byte(content), 0o644) - require.NoError(t, err) -} diff --git a/sdk/go/openshell/v1/gateway/doc.go b/sdk/go/openshell/v1/gateway/doc.go deleted file mode 100644 index 52349705c6..0000000000 --- a/sdk/go/openshell/v1/gateway/doc.go +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package gateway reads on-disk gateway configurations created by the -// OpenShell Rust CLI and constructs fully wired SDK clients. -// -// The package resolves XDG config paths, validates gateway names, loads -// tokens lazily, maps auth modes to existing auth providers, and provides -// one-call convenience constructors. This eliminates 20+ lines of -// boilerplate for Go programs connecting to gateways managed by the CLI. -// -// # Quick Start -// -// Connect to a named gateway: -// -// client, err := gateway.NewClient("prod") -// if err != nil { -// log.Fatal(err) -// } -// defer client.Close() -// -// Connect to the active gateway (set via `openshell gateway use`): -// -// client, err := gateway.NewClient("") -// if err != nil { -// log.Fatal(err) -// } -// defer client.Close() -// -// Inspect configuration without creating a client: -// -// cfg, err := gateway.LoadConfig("staging") -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("Endpoint: %s, Auth: %s\n", cfg.Endpoint, cfg.AuthMode) -// -// List all configured gateways: -// -// gateways, err := gateway.ListGateways() -// if err != nil { -// log.Fatal(err) -// } -// for _, gw := range gateways { -// fmt.Printf("%s (active=%v, source=%s)\n", gw.Name, gw.Active, gw.Source) -// } -// -// # On-Disk Layout -// -// The package reads gateway metadata from the following locations: -// -// $XDG_CONFIG_HOME/openshell/gateways//metadata.json (user) -// /etc/openshell/gateways//metadata.json (system) -// -// Token files (edge_token, cf_token, oidc_token.json) sit alongside -// metadata.json and are loaded lazily on first authentication attempt. -// -// # Error Handling -// -// The package provides typed errors for precise failure classification: -// -// - [ErrGatewayNotFound]: no gateway directory found -// - [ErrConfigParse]: metadata.json missing or malformed -// - [ErrTokenLoad]: token file missing, unreadable, malformed, or expired; RPCs report it as Unauthenticated -// - [ErrUnsupportedAuthMode]: unrecognized auth_mode value -// - [ErrInvalidGatewayName]: name fails validation -// - [ErrNoActiveGateway]: no active gateway configured -// -// All errors support [errors.Is] for classification. -// -// # Thread Safety -// -// All exported functions are safe for concurrent use from multiple -// goroutines. -package gateway diff --git a/sdk/go/openshell/v1/gateway/errors.go b/sdk/go/openshell/v1/gateway/errors.go deleted file mode 100644 index b2e09bf1bb..0000000000 --- a/sdk/go/openshell/v1/gateway/errors.go +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package gateway - -import "errors" - -// Sentinel errors for gateway configuration failures. All wrapped errors -// returned by this package support classification via [errors.Is]. -var ( - // ErrGatewayNotFound is returned when no gateway directory exists - // in either the user or system config paths. - ErrGatewayNotFound = errors.New("gateway: not found") - - // ErrConfigParse is returned when metadata.json is missing, - // unreadable, or contains invalid JSON. - ErrConfigParse = errors.New("gateway: config parse error") - - // ErrTokenLoad is returned when a token file (edge_token, - // oidc_token.json) is missing, unreadable, malformed, or expired. - ErrTokenLoad = errors.New("gateway: token load error") - - // ErrUnsupportedAuthMode is returned when the auth_mode value in - // metadata.json is not recognized (not none, plaintext, - // cloudflare_jwt, oidc, or mtls). - ErrUnsupportedAuthMode = errors.New("gateway: unsupported auth mode") - - // ErrInvalidGatewayName is returned when a gateway name fails - // validation (empty, contains path separators, dots, or - // non-ASCII-alnum-dash-underscore characters). - ErrInvalidGatewayName = errors.New("gateway: invalid gateway name") - - // ErrNoActiveGateway is returned when no active gateway is - // configured (active_gateway file missing or empty). - ErrNoActiveGateway = errors.New("gateway: no active gateway") -) diff --git a/sdk/go/openshell/v1/gateway/errors_test.go b/sdk/go/openshell/v1/gateway/errors_test.go deleted file mode 100644 index 09d8b40d6d..0000000000 --- a/sdk/go/openshell/v1/gateway/errors_test.go +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package gateway - -import ( - "errors" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" -) - -// --- T011: Error type tests --- - -func TestSentinelErrors_ErrorsIs(t *testing.T) { - sentinels := []struct { - name string - err error - }{ - {"ErrGatewayNotFound", ErrGatewayNotFound}, - {"ErrConfigParse", ErrConfigParse}, - {"ErrTokenLoad", ErrTokenLoad}, - {"ErrUnsupportedAuthMode", ErrUnsupportedAuthMode}, - {"ErrInvalidGatewayName", ErrInvalidGatewayName}, - {"ErrNoActiveGateway", ErrNoActiveGateway}, - } - - for _, tc := range sentinels { - t.Run(tc.name+"_direct", func(t *testing.T) { - assert.True(t, errors.Is(tc.err, tc.err), - "errors.Is should match sentinel directly") - }) - - t.Run(tc.name+"_wrapped", func(t *testing.T) { - wrapped := fmt.Errorf("context: %w", tc.err) - assert.True(t, errors.Is(wrapped, tc.err), - "errors.Is should match through fmt.Errorf wrapping") - }) - - t.Run(tc.name+"_double_wrapped", func(t *testing.T) { - inner := fmt.Errorf("inner: %w", tc.err) - outer := fmt.Errorf("outer: %w", inner) - assert.True(t, errors.Is(outer, tc.err), - "errors.Is should match through double wrapping") - }) - } -} - -func TestSentinelErrors_NotConfused(t *testing.T) { - // Verify that different sentinel errors are not equal. - pairs := []struct { - a, b error - }{ - {ErrGatewayNotFound, ErrConfigParse}, - {ErrConfigParse, ErrTokenLoad}, - {ErrTokenLoad, ErrUnsupportedAuthMode}, - {ErrUnsupportedAuthMode, ErrInvalidGatewayName}, - {ErrInvalidGatewayName, ErrNoActiveGateway}, - {ErrNoActiveGateway, ErrGatewayNotFound}, - } - - for _, tc := range pairs { - t.Run(tc.a.Error()+"_vs_"+tc.b.Error(), func(t *testing.T) { - assert.False(t, errors.Is(tc.a, tc.b), - "different sentinels must not match") - }) - } -} - -func TestSentinelErrors_HaveMessages(t *testing.T) { - sentinels := []error{ - ErrGatewayNotFound, - ErrConfigParse, - ErrTokenLoad, - ErrUnsupportedAuthMode, - ErrInvalidGatewayName, - ErrNoActiveGateway, - } - - for _, err := range sentinels { - t.Run(err.Error(), func(t *testing.T) { - msg := err.Error() - assert.NotEmpty(t, msg) - assert.Contains(t, msg, "gateway:") - }) - } -} diff --git a/sdk/go/openshell/v1/gateway/gateway.go b/sdk/go/openshell/v1/gateway/gateway.go deleted file mode 100644 index e7ff5e3576..0000000000 --- a/sdk/go/openshell/v1/gateway/gateway.go +++ /dev/null @@ -1,182 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package gateway - -import ( - "fmt" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// NewClient creates a fully wired SDK client from an on-disk gateway -// configuration. If name is empty, the active gateway (set via -// `openshell gateway use`, or the system default) is used. -// -// The function resolves the gateway directory, parses metadata.json, -// loads tokens lazily, maps the auth mode to an SDK auth provider, and -// applies any ClientOptions before delegating to [v1.NewClient]. -// -// NewClient is safe for concurrent use from multiple goroutines. -func NewClient(name string, opts ...ClientOption) (*v1.Client, error) { - cfg, err := loadConfigInternal(name) - if err != nil { - return nil, err - } - - // Apply caller options. - cc := &clientConfig{} - options.Apply(cc, opts) - - // Resolve auth provider: caller override takes precedence. - auth := cc.auth - if auth == nil { - auth, err = resolveAuthProvider(cfg) - if err != nil { - return nil, err - } - } - - // Build the SDK Config. - sdkCfg := types.Config{ - Address: cfg.Endpoint, - Auth: auth, - } - - // Apply TLS: caller override or auth-mode defaults. - if cc.tls != nil { - sdkCfg.TLS = cc.tls - } else if cfg.AuthMode == AuthModePlaintext { - sdkCfg.TLS = &types.TLSConfig{Insecure: true} - } - - if cc.timeout > 0 { - sdkCfg.Timeout = cc.timeout - } - if cc.retryPolicy != nil { - sdkCfg.RetryPolicy = cc.retryPolicy - } - if cc.logger != nil { - sdkCfg.Logger = cc.logger - } - - return v1.NewClient(sdkCfg) -} - -// LoadConfig reads and parses a gateway's on-disk configuration without -// creating a client connection. If name is empty, the active gateway (user -// selection, then system default) is used. -// -// The returned [Config] is an immutable snapshot; changes to the on-disk -// files after this call are not reflected. -// -// LoadConfig is safe for concurrent use from multiple goroutines. -func LoadConfig(name string) (*Config, error) { - return loadConfigInternal(name) -} - -// loadConfigInternal resolves the gateway name (including active gateway -// fallback), finds the config directory, and parses metadata.json. This -// shared implementation is used by both NewClient and LoadConfig. -func loadConfigInternal(name string) (*Config, error) { - // If name is empty, resolve the active gateway. - if name == "" { - activeName, err := resolveActiveGateway() - if err != nil { - return nil, err - } - name = activeName - } - - dir, source, err := resolveGatewayDir(name) - if err != nil { - return nil, err - } - - cfg, err := parseMetadata(dir) - if err != nil { - return nil, err - } - - // Override name from directory (validated) rather than metadata.json. - cfg.Name = name - cfg.Source = source - - return cfg, nil -} - -// ListGateways enumerates all available gateways from user and system -// directories. User gateways appear first. If the same name exists in -// both directories, only the user gateway is returned (user precedence). -// Returns an empty slice (not an error) when no gateways are configured. -// Active also reflects the system default when no user selection exists. -// -// ListGateways is safe for concurrent use from multiple goroutines. -func ListGateways() ([]Info, error) { - seen := make(map[string]bool) - var result []Info - - activeName, _ := resolveActiveGateway() - - userBase, err := userConfigDir() - if err == nil { - names, listErr := listGatewayDirs(userBase) - if listErr != nil { - return nil, listErr - } - for _, name := range names { - seen[name] = true - result = append(result, Info{ - Name: name, - Active: name == activeName, - Source: SourceUser, - }) - } - } - - sysNames, listErr := listGatewayDirs(systemConfigDir()) - if listErr != nil { - return nil, listErr - } - for _, name := range sysNames { - if !seen[name] { - result = append(result, Info{ - Name: name, - Active: name == activeName, - Source: SourceSystem, - }) - } - } - - return result, nil -} - -// resolveAuthProvider maps a Config's AuthMode to an SDK AuthProvider. -// Tokens are loaded lazily where possible. -func resolveAuthProvider(cfg *Config) (types.AuthProvider, error) { - switch cfg.AuthMode { - case AuthModeNone: - return v1.NoAuth(), nil - - case AuthModePlaintext: - return v1.NoAuth(), nil - - case AuthModeCloudflareJWT: - // Token loading is deferred to GetRequestMetadata so that - // NewClient succeeds even when the token file is missing. - // The error surfaces on first authentication attempt (FR-007). - return &lazyEdgeAuth{loader: &edgeTokenLoader{dir: cfg.Dir}}, nil - - case AuthModeOIDC: - src := newDiskTokenSource(cfg.Dir, cfg.Name) - return v1.RefreshableToken(src) - - case AuthModeMTLS: - return nil, fmt.Errorf("%w: mtls is not yet supported; use WithAuth() to provide a custom auth provider", ErrUnsupportedAuthMode) - - default: - return nil, fmt.Errorf("%w: %q", ErrUnsupportedAuthMode, cfg.AuthMode) - } -} diff --git a/sdk/go/openshell/v1/gateway/gateway_test.go b/sdk/go/openshell/v1/gateway/gateway_test.go deleted file mode 100644 index a2eee56061..0000000000 --- a/sdk/go/openshell/v1/gateway/gateway_test.go +++ /dev/null @@ -1,534 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package gateway - -import ( - "context" - "fmt" - "os" - "path/filepath" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/oidctoken" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- Test helpers --- - -// setupGateway creates a gateway directory with the given metadata.json -// content under a temp XDG config dir. Returns the XDG root path. -func setupGateway(t *testing.T, name, metadataJSON string) string { - t.Helper() - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - - gwDir := filepath.Join(tmp, "openshell", "gateways", name) - require.NoError(t, os.MkdirAll(gwDir, 0o755)) - writeFile(t, gwDir, "metadata.json", metadataJSON) - - return tmp -} - -// setupGatewayWithTokens creates a gateway directory with metadata and -// token files. -func setupGatewayWithTokens(t *testing.T, name, metadataJSON string, tokens map[string]string) string { - t.Helper() - tmp := setupGateway(t, name, metadataJSON) - - gwDir := filepath.Join(tmp, "openshell", "gateways", name) - for filename, content := range tokens { - writeFile(t, gwDir, filename, content) - } - - return tmp -} - -// --- T018: NewClient tests --- - -func TestNewClient_AuthModeNone(t *testing.T) { - setupGateway(t, "test-gw", `{"gateway_endpoint":"localhost:50051","auth_mode":"none"}`) - - client, err := NewClient("test-gw", WithTLS(&types.TLSConfig{Insecure: true})) - require.NoError(t, err) - require.NotNil(t, client) - assert.NoError(t, client.Close()) -} - -func TestNewClient_AuthModePlaintext(t *testing.T) { - setupGateway(t, "plain-gw", `{"gateway_endpoint":"localhost:50051","auth_mode":"plaintext"}`) - - // Plaintext mode should auto-set insecure TLS. - client, err := NewClient("plain-gw") - require.NoError(t, err) - require.NotNil(t, client) - assert.NoError(t, client.Close()) -} - -func TestNewClient_AuthModeCloudflareJWT(t *testing.T) { - setupGatewayWithTokens(t, "cf-gw", - `{"gateway_endpoint":"localhost:50051","auth_mode":"cloudflare_jwt"}`, - map[string]string{edgeTokenFile: "test-edge-token"}, - ) - - // StaticToken requires transport security, so use WithAuth override - // to bypass gRPC TLS requirement. Auth resolution is verified by - // TestResolveAuthProvider_CloudflareJWT. - client, err := NewClient("cf-gw", - WithAuth(&mockAuth{}), - WithTLS(&types.TLSConfig{Insecure: true}), - ) - require.NoError(t, err) - require.NotNil(t, client) - assert.NoError(t, client.Close()) -} - -func TestNewClient_AuthModeOIDC(t *testing.T) { - setupGatewayWithTokens(t, "oidc-gw", - `{"gateway_endpoint":"localhost:50051","auth_mode":"oidc"}`, - map[string]string{oidctoken.FileName: oidcBundleJSON("test-oidc-token", "")}, - ) - - // RefreshableToken requires transport security, so use WithAuth - // override. Auth resolution verified by TestResolveAuthProvider_OIDC. - client, err := NewClient("oidc-gw", - WithAuth(&mockAuth{}), - WithTLS(&types.TLSConfig{Insecure: true}), - ) - require.NoError(t, err) - require.NotNil(t, client) - assert.NoError(t, client.Close()) -} - -func TestNewClient_NotFound(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - - _, err := NewClient("nonexistent") - require.Error(t, err) - assert.ErrorIs(t, err, ErrGatewayNotFound) -} - -func TestNewClient_InvalidName(t *testing.T) { - _, err := NewClient("../escape") - require.Error(t, err) - assert.ErrorIs(t, err, ErrInvalidGatewayName) -} - -func TestNewClient_MissingEdgeToken(t *testing.T) { - setupGateway(t, "no-token-gw", `{"gateway_endpoint":"localhost:50051","auth_mode":"cloudflare_jwt"}`) - - // Edge token loading is lazy (FR-007): NewClient succeeds even when - // the token file is missing. The error surfaces on first use via - // GetRequestMetadata, not at construction time. - _, err := NewClient("no-token-gw") - require.NoError(t, err) -} - -func TestNewClient_MissingOIDCToken(t *testing.T) { - setupGateway(t, "no-oidc-gw", `{"gateway_endpoint":"localhost:50051","auth_mode":"oidc"}`) - - _, err := NewClient("no-oidc-gw") - // OIDC uses RefreshableToken which defers the disk read to Token(), - // so NewClient should succeed. The error would come on first use. - // Let's verify it doesn't fail at construction time. - require.NoError(t, err) - assert.NoError(t, err) -} - -func TestNewClient_MTLSUnsupported(t *testing.T) { - setupGateway(t, "mtls-gw", `{"gateway_endpoint":"localhost:50051","auth_mode":"mtls"}`) - - _, err := NewClient("mtls-gw") - require.Error(t, err) - assert.ErrorIs(t, err, ErrUnsupportedAuthMode) - assert.Contains(t, err.Error(), "mtls") -} - -func TestNewClient_WithAuthOverride(t *testing.T) { - setupGateway(t, "override-gw", `{"gateway_endpoint":"localhost:50051","auth_mode":"cloudflare_jwt"}`) - - // Even though auth_mode is cloudflare_jwt and there's no edge_token, - // the WithAuth override should bypass token loading entirely. - customAuth := &mockAuth{} - client, err := NewClient("override-gw", - WithAuth(customAuth), - WithTLS(&types.TLSConfig{Insecure: true}), - ) - require.NoError(t, err) - require.NotNil(t, client) - assert.NoError(t, client.Close()) -} - -func TestNewClient_WithOptions(t *testing.T) { - setupGateway(t, "opts-gw", `{"gateway_endpoint":"localhost:50051","auth_mode":"none"}`) - - client, err := NewClient("opts-gw", - WithTLS(&types.TLSConfig{Insecure: true}), - ) - require.NoError(t, err) - require.NotNil(t, client) - assert.NoError(t, client.Close()) -} - -// --- T019: Credential leak test --- - -func TestNewClient_NoCredentialLeaks(t *testing.T) { - // Test 1: Invalid gateway with path traversal attempt. - _, err := NewClient("../../../etc/passwd") - require.Error(t, err) - assert.NotContains(t, err.Error(), "passwd") - - // Test 2: Verify error from missing edge token does not reveal - // file system details beyond the generic message. - setupGateway(t, "no-edge-gw", `{"gateway_endpoint":"localhost:50051","auth_mode":"cloudflare_jwt"}`) - cfg := &Config{ - AuthMode: AuthModeCloudflareJWT, - Dir: filepath.Join(os.Getenv("XDG_CONFIG_HOME"), "openshell", "gateways", "no-edge-gw"), - } - auth, authErr := resolveAuthProvider(cfg) - require.NoError(t, authErr) - _, err = auth.GetRequestMetadata(context.Background()) - require.Error(t, err) - assert.ErrorIs(t, err, ErrTokenLoad) - - // Test 3: Verify credential values never appear in error strings. - secretToken := "SUPER_SECRET_TOKEN_12345" - setupGatewayWithTokens(t, "leak-gw", - `{"gateway_endpoint":"localhost:50051","auth_mode":"cloudflare_jwt"}`, - map[string]string{edgeTokenFile: secretToken}, - ) - // Use resolveAuthProvider directly to test token loading. - cfg = &Config{ - Name: "leak-gw", - Endpoint: "localhost:50051", - AuthMode: AuthModeCloudflareJWT, - Dir: filepath.Join(os.Getenv("XDG_CONFIG_HOME"), "openshell", "gateways", "leak-gw"), - } - auth, err = resolveAuthProvider(cfg) - require.NoError(t, err) - - // The provider should not expose the token in its string form. - if stringer, ok := auth.(interface{ String() string }); ok { - assert.NotContains(t, stringer.String(), secretToken) - } -} - -func TestResolveAuthProvider_NoTokenLeaks(t *testing.T) { - secretToken := "CREDENTIAL_THAT_MUST_NOT_LEAK" - - // Create a gateway with a bad OIDC token. - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - gwDir := filepath.Join(tmp, "openshell", "gateways", "leak-test") - require.NoError(t, os.MkdirAll(gwDir, 0o755)) - writeFile(t, gwDir, "metadata.json", `{"gateway_endpoint":"localhost:50051","auth_mode":"cloudflare_jwt"}`) - writeFile(t, gwDir, edgeTokenFile, secretToken) - - cfg := &Config{ - Name: "leak-test", - Endpoint: "localhost:50051", - AuthMode: AuthModeCloudflareJWT, - Dir: gwDir, - } - - auth, err := resolveAuthProvider(cfg) - require.NoError(t, err) - - // The auth provider should work but the token value should not - // appear in the provider's string representation (if any). - providerStr := "" - if stringer, ok := auth.(interface{ String() string }); ok { - providerStr = stringer.String() - assert.NotContains(t, providerStr, secretToken) - } - - // Verify the token IS used correctly via GetRequestMetadata. - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Contains(t, md["authorization"], secretToken) -} - -// --- Test helpers --- - -// mockAuth is a minimal AuthProvider for testing WithAuth overrides. -type mockAuth struct{} - -func (m *mockAuth) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) { - return map[string]string{"authorization": "Bearer mock-token"}, nil -} - -func (m *mockAuth) RequireTransportSecurity() bool { - return false -} - -// --- LoadConfig tests (T025 placeholder, implemented here for Phase 3 coverage) --- - -func TestLoadConfig_ValidConfig(t *testing.T) { - setupGateway(t, "cfg-gw", `{"gateway_endpoint":"host:443","auth_mode":"oidc","name":"ignored"}`) - - cfg, err := LoadConfig("cfg-gw") - require.NoError(t, err) - // Name comes from directory, not metadata.json "name" field. - assert.Equal(t, "cfg-gw", cfg.Name) - assert.Equal(t, "host:443", cfg.Endpoint) - assert.Equal(t, AuthModeOIDC, cfg.AuthMode) - assert.Equal(t, SourceUser, cfg.Source) - assert.NotEmpty(t, cfg.Dir) -} - -func TestLoadConfig_NotFound(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - - _, err := LoadConfig("missing-gw") - require.Error(t, err) - assert.ErrorIs(t, err, ErrGatewayNotFound) -} - -func TestLoadConfig_FrozenSnapshot(t *testing.T) { - xdg := setupGateway(t, "snap-gw", `{"gateway_endpoint":"original:443","auth_mode":"none"}`) - - cfg, err := LoadConfig("snap-gw") - require.NoError(t, err) - assert.Equal(t, "original:443", cfg.Endpoint) - - // Modify the on-disk file. - gwDir := filepath.Join(xdg, "openshell", "gateways", "snap-gw") - writeFile(t, gwDir, "metadata.json", `{"gateway_endpoint":"modified:443","auth_mode":"none"}`) - - // The previously loaded config should be unchanged. - assert.Equal(t, "original:443", cfg.Endpoint) - - // A new load should see the change. - cfg2, err := LoadConfig("snap-gw") - require.NoError(t, err) - assert.Equal(t, "modified:443", cfg2.Endpoint) -} - -// --- resolveAuthProvider tests --- - -func TestResolveAuthProvider_None(t *testing.T) { - cfg := &Config{AuthMode: AuthModeNone} - auth, err := resolveAuthProvider(cfg) - require.NoError(t, err) - require.NotNil(t, auth) - assert.False(t, auth.RequireTransportSecurity()) -} - -func TestResolveAuthProvider_Plaintext(t *testing.T) { - cfg := &Config{AuthMode: AuthModePlaintext} - auth, err := resolveAuthProvider(cfg) - require.NoError(t, err) - require.NotNil(t, auth) - assert.False(t, auth.RequireTransportSecurity()) -} - -func TestResolveAuthProvider_CloudflareJWT(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, edgeTokenFile, "cf-jwt-token") - - cfg := &Config{AuthMode: AuthModeCloudflareJWT, Dir: dir} - auth, err := resolveAuthProvider(cfg) - require.NoError(t, err) - require.NotNil(t, auth) - - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer cf-jwt-token", md["authorization"]) -} - -func TestResolveAuthProvider_OIDC(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, oidctoken.FileName, oidcBundleJSON("oidc-access-token", "")) - - cfg := &Config{AuthMode: AuthModeOIDC, Dir: dir, Name: "prod"} - auth, err := resolveAuthProvider(cfg) - require.NoError(t, err) - require.NotNil(t, auth) - - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer oidc-access-token", md["authorization"]) -} - -func TestResolveAuthProvider_OIDCPicksUpReplacedTokenNearExpiry(t *testing.T) { - dir := t.TempDir() - // Within the default 10s refresh leeway, so the next call re-reads the file. - writeFile(t, dir, oidctoken.FileName, oidcBundleJSON("token-a", fmt.Sprint(time.Now().Add(5*time.Second).Unix()))) - - auth, err := resolveAuthProvider(&Config{AuthMode: AuthModeOIDC, Dir: dir, Name: "prod"}) - require.NoError(t, err) - - md, err := auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer token-a", md["authorization"]) - - writeFile(t, dir, oidctoken.FileName, oidcBundleJSON("token-b", fmt.Sprint(time.Now().Add(time.Hour).Unix()))) - - md, err = auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, "Bearer token-b", md["authorization"]) -} - -func TestResolveAuthProvider_OIDCLegacyFileReturnsLoginHint(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, oidctoken.FileName, `{"access_token": "legacy-token", "expiry": "2099-01-01T00:00:00Z"}`) - - auth, err := resolveAuthProvider(&Config{AuthMode: AuthModeOIDC, Dir: dir, Name: "prod"}) - require.NoError(t, err) - - // The second call is answered from the refresh backoff. - for range 2 { - _, err = auth.GetRequestMetadata(context.Background()) - require.ErrorIs(t, err, ErrTokenLoad) - assert.Contains(t, err.Error(), loginHint) - } -} - -func TestResolveAuthProvider_MTLS(t *testing.T) { - cfg := &Config{AuthMode: AuthModeMTLS} - _, err := resolveAuthProvider(cfg) - require.Error(t, err) - assert.ErrorIs(t, err, ErrUnsupportedAuthMode) - assert.Contains(t, err.Error(), "mtls") - assert.Contains(t, err.Error(), "WithAuth") -} - -func TestResolveAuthProvider_UnknownMode(t *testing.T) { - cfg := &Config{AuthMode: "alien_auth"} - _, err := resolveAuthProvider(cfg) - require.Error(t, err) - assert.ErrorIs(t, err, ErrUnsupportedAuthMode) -} - -// --- T021/T023: Active gateway tests --- - -func TestNewClient_ActiveGateway(t *testing.T) { - tmp := setupGateway(t, "active-gw", `{"gateway_endpoint":"localhost:50051","auth_mode":"none"}`) - - activeFile := filepath.Join(tmp, "openshell", "active_gateway") - writeFile(t, filepath.Join(tmp, "openshell"), "active_gateway", "active-gw") - _ = activeFile - - client, err := NewClient("", WithTLS(&types.TLSConfig{Insecure: true})) - require.NoError(t, err) - require.NotNil(t, client) - assert.NoError(t, client.Close()) -} - -func TestNewClient_NoActiveGateway(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - t.Setenv(systemGatewayDirEnv, t.TempDir()) - require.NoError(t, os.MkdirAll(filepath.Join(tmp, "openshell"), 0o755)) - - _, err := NewClient("") - require.Error(t, err) - assert.ErrorIs(t, err, ErrNoActiveGateway) -} - -func TestLoadConfig_SystemActiveGateway(t *testing.T) { - user, system := t.TempDir(), t.TempDir() - t.Setenv("XDG_CONFIG_HOME", user) - t.Setenv(systemGatewayDirEnv, system) - writeFile(t, system, "active_gateway", "corp") - gwDir := filepath.Join(system, "gateways", "corp") - require.NoError(t, os.MkdirAll(gwDir, 0o755)) - writeFile(t, gwDir, "metadata.json", `{"gateway_endpoint":"host:443"}`) - - cfg, err := LoadConfig("") - require.NoError(t, err) - assert.Equal(t, "corp", cfg.Name) - assert.Equal(t, SourceSystem, cfg.Source) -} - -func TestLoadConfig_ActiveGateway(t *testing.T) { - tmp := setupGateway(t, "my-active", `{"gateway_endpoint":"host:443","auth_mode":"oidc"}`) - writeFile(t, filepath.Join(tmp, "openshell"), "active_gateway", "my-active") - - cfg, err := LoadConfig("") - require.NoError(t, err) - assert.Equal(t, "my-active", cfg.Name) - assert.Equal(t, "host:443", cfg.Endpoint) -} - -// --- T027: ListGateways tests --- - -func TestListGateways_MultipleGateways(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - t.Setenv(systemGatewayDirEnv, tmp) - - for _, name := range []string{"prod", "staging", "dev"} { - gwDir := filepath.Join(tmp, "openshell", "gateways", name) - require.NoError(t, os.MkdirAll(gwDir, 0o755)) - } - - gateways, err := ListGateways() - require.NoError(t, err) - assert.Len(t, gateways, 3) - - names := make(map[string]bool) - for _, gw := range gateways { - names[gw.Name] = true - assert.Equal(t, SourceUser, gw.Source) - } - assert.True(t, names["prod"]) - assert.True(t, names["staging"]) - assert.True(t, names["dev"]) -} - -func TestListGateways_EmptyDirs(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - t.Setenv(systemGatewayDirEnv, tmp) - - gateways, err := ListGateways() - require.NoError(t, err) - assert.Empty(t, gateways) -} - -func TestListGateways_ActiveStatus(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - t.Setenv(systemGatewayDirEnv, tmp) - - for _, name := range []string{"alpha", "beta"} { - gwDir := filepath.Join(tmp, "openshell", "gateways", name) - require.NoError(t, os.MkdirAll(gwDir, 0o755)) - } - writeFile(t, filepath.Join(tmp, "openshell"), "active_gateway", "beta") - - gateways, err := ListGateways() - require.NoError(t, err) - assert.Len(t, gateways, 2) - - for _, gw := range gateways { - if gw.Name == "beta" { - assert.True(t, gw.Active) - } else { - assert.False(t, gw.Active) - } - } -} - -func TestListGateways_SystemActiveStatus(t *testing.T) { - user, system := t.TempDir(), t.TempDir() - t.Setenv("XDG_CONFIG_HOME", user) - t.Setenv(systemGatewayDirEnv, system) - require.NoError(t, os.MkdirAll(filepath.Join(user, "openshell", "gateways", "mine"), 0o755)) - require.NoError(t, os.MkdirAll(filepath.Join(system, "gateways", "corp"), 0o755)) - writeFile(t, system, "active_gateway", "corp") - - gateways, err := ListGateways() - require.NoError(t, err) - require.Len(t, gateways, 2) - - assert.Equal(t, Info{Name: "mine", Active: false, Source: SourceUser}, gateways[0]) - assert.Equal(t, Info{Name: "corp", Active: true, Source: SourceSystem}, gateways[1]) -} diff --git a/sdk/go/openshell/v1/gateway/options.go b/sdk/go/openshell/v1/gateway/options.go deleted file mode 100644 index dfd16e89ca..0000000000 --- a/sdk/go/openshell/v1/gateway/options.go +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package gateway - -import ( - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// clientConfig holds resolved options applied after gateway config -// resolution but before the v1.Client is created. -type clientConfig struct { - logger types.Logger - timeout time.Duration - tls *types.TLSConfig - auth types.AuthProvider - retryPolicy *types.RetryPolicy -} - -// ClientOption configures the behavior of [NewClient]. Options are -// applied after gateway configuration is resolved but before the -// underlying SDK client is created. -type ClientOption func(*clientConfig) - -// WithLogger sets the logger on the SDK client configuration. -func WithLogger(l types.Logger) ClientOption { - return func(c *clientConfig) { - c.logger = l - } -} - -// WithTimeout sets the connection timeout for the SDK client. -func WithTimeout(d time.Duration) ClientOption { - return func(c *clientConfig) { - c.timeout = d - } -} - -// WithTLS overrides the TLS settings derived from the gateway's auth mode. -// Use this to provide custom certificates or force insecure connections. -func WithTLS(cfg *types.TLSConfig) ClientOption { - return func(c *clientConfig) { - c.tls = cfg - } -} - -// WithAuth overrides the auth provider that would normally be resolved -// from the gateway's auth_mode. When set, the gateway package skips -// its own auth resolution and uses the provided provider directly. -func WithAuth(provider types.AuthProvider) ClientOption { - return func(c *clientConfig) { - c.auth = provider - } -} - -// WithRetryPolicy sets the retry policy on the SDK client configuration. -func WithRetryPolicy(p *types.RetryPolicy) ClientOption { - return func(c *clientConfig) { - c.retryPolicy = p - } -} diff --git a/sdk/go/openshell/v1/gateway/options_nil_test.go b/sdk/go/openshell/v1/gateway/options_nil_test.go deleted file mode 100644 index 8aa8061d15..0000000000 --- a/sdk/go/openshell/v1/gateway/options_nil_test.go +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package gateway - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" -) - -func TestClientOption_NilIgnored(t *testing.T) { - var cfg clientConfig - options.Apply(&cfg, []ClientOption{ - WithTimeout(30 * time.Second), - nil, - WithLogger(nil), - }) - assert.Equal(t, 30*time.Second, cfg.timeout) -} diff --git a/sdk/go/openshell/v1/gateway/paths.go b/sdk/go/openshell/v1/gateway/paths.go deleted file mode 100644 index 00c67d050b..0000000000 --- a/sdk/go/openshell/v1/gateway/paths.go +++ /dev/null @@ -1,199 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package gateway - -import ( - "fmt" - "os" - "path/filepath" - "strings" -) - -const ( - // appName is the application directory name used in XDG paths. - appName = "openshell" - - // gatewaySubdir is the subdirectory within the app config holding - // per-gateway directories. - gatewaySubdir = "gateways" - - // activeGatewayFile is the filename that stores the active gateway name. - activeGatewayFile = "active_gateway" - - // systemConfigBase is the system-wide config directory. - systemConfigBase = "/etc/openshell" - - // systemGatewayDirEnv overrides the system-wide config root. Keep this in - // sync with the Rust CLI so SDK discovery sees the same gateway set. - systemGatewayDirEnv = "OPENSHELL_SYSTEM_GATEWAY_DIR" -) - -// userConfigDir returns the user-specific configuration directory for -// OpenShell, following XDG Base Directory specification: -// -// $XDG_CONFIG_HOME/openshell (if XDG_CONFIG_HOME is set) -// ~/.config/openshell (fallback) -func userConfigDir() (string, error) { - if xdg := os.Getenv("XDG_CONFIG_HOME"); xdg != "" { - if !filepath.IsAbs(xdg) { - return "", fmt.Errorf("XDG_CONFIG_HOME must be an absolute path, got %q", xdg) - } - return filepath.Join(xdg, appName), nil - } - - home, err := os.UserHomeDir() - if err != nil { - return "", fmt.Errorf("cannot determine home directory: %w", err) - } - - return filepath.Join(home, ".config", appName), nil -} - -// systemGatewayDir returns the system-wide gateway config directory. -func systemGatewayDir() string { - return filepath.Join(systemConfigDir(), gatewaySubdir) -} - -// systemConfigDir returns the system-wide configuration root. Empty and -// relative overrides are ignored to match the CLI's fail-safe behavior. -func systemConfigDir() string { - if dir := os.Getenv(systemGatewayDirEnv); dir != "" && filepath.IsAbs(dir) { - return dir - } - return systemConfigBase -} - -// resolveGatewayDir searches for a gateway directory by name, checking the -// user directory first, then the system directory. Returns the absolute -// directory path, the config source, or ErrGatewayNotFound. -func resolveGatewayDir(name string) (string, ConfigSource, error) { - if err := validateGatewayName(name); err != nil { - return "", "", err - } - - // Check user config dir first. - userBase, err := userConfigDir() - if err == nil { - userDir := filepath.Join(userBase, gatewaySubdir, name) - if info, statErr := os.Stat(userDir); statErr == nil && info.IsDir() { - return userDir, SourceUser, nil - } - } - - // Check system config dir. - sysDir := filepath.Join(systemGatewayDir(), name) - if info, statErr := os.Stat(sysDir); statErr == nil && info.IsDir() { - return sysDir, SourceSystem, nil - } - - return "", "", fmt.Errorf("%w: %q", ErrGatewayNotFound, name) -} - -// validateGatewayName accepts portable, single-component gateway names. -// Host-derived FQDN and IPv4 names may contain dots. Windows device names, -// reserved filename characters, and ambiguous dot components are rejected -// on every platform to keep SDK behavior consistent. -func validateGatewayName(name string) error { - if name == "" { - return fmt.Errorf("%w: name must not be empty", ErrInvalidGatewayName) - } - if name == "." || name == ".." { - return fmt.Errorf("%w: invalid path component", ErrInvalidGatewayName) - } - if strings.ContainsAny(name, `/\`) { - return fmt.Errorf("%w: name must not contain path separators", ErrInvalidGatewayName) - } - if strings.ContainsRune(name, '\x00') { - return fmt.Errorf("%w: name must not contain NUL", ErrInvalidGatewayName) - } - if strings.HasPrefix(name, ".") || strings.HasSuffix(name, ".") || strings.Contains(name, "..") { - return fmt.Errorf("%w: invalid dot placement", ErrInvalidGatewayName) - } - - // This ASCII-only grammar excludes Windows reserved filename characters - // (including colons in raw IPv6 addresses), spaces, and control characters. - for _, r := range name { - if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || - (r >= '0' && r <= '9') || r == '-' || r == '_' || r == '.' { - continue - } - return fmt.Errorf("%w: non-portable character %q", ErrInvalidGatewayName, r) - } - - // Windows treats device names as reserved even with an extension: - // e.g. CON.txt and COM1.example.com refer to device names. - stem, _, _ := strings.Cut(name, ".") - stem = strings.ToUpper(stem) - switch stem { - case "CON", "PRN", "AUX", "NUL": - return fmt.Errorf("%w: reserved Windows device name", ErrInvalidGatewayName) - } - // Reject COM0/LPT0 conservatively too, for Windows portability. - if len(stem) == 4 && (strings.HasPrefix(stem, "COM") || strings.HasPrefix(stem, "LPT")) && - stem[3] >= '0' && stem[3] <= '9' { - return fmt.Errorf("%w: reserved Windows device name", ErrInvalidGatewayName) - } - return nil -} - -// resolveActiveGateway returns the validated active gateway name. It reads -// the user active_gateway file and falls back to the system one when the user -// file is missing, empty, or invalid, matching the CLI. Returns -// ErrNoActiveGateway describing the user file when neither yields a name. -func resolveActiveGateway() (string, error) { - userBase, err := userConfigDir() - if err != nil { - err = fmt.Errorf("%w: cannot determine config directory: %v", ErrNoActiveGateway, err) - } else { - var name string - if name, err = readActiveGatewayFile(filepath.Join(userBase, activeGatewayFile)); err == nil { - return name, nil - } - } - - if name, sysErr := readActiveGatewayFile(filepath.Join(systemConfigDir(), activeGatewayFile)); sysErr == nil { - return name, nil - } - return "", err -} - -// readActiveGatewayFile reads and validates the gateway name stored at path. -func readActiveGatewayFile(path string) (string, error) { - data, err := os.ReadFile(path) - if err != nil { - return "", fmt.Errorf("%w", ErrNoActiveGateway) - } - - name := strings.TrimSpace(string(data)) - if name == "" { - return "", fmt.Errorf("%w: active_gateway file is empty", ErrNoActiveGateway) - } - - if err := validateGatewayName(name); err != nil { - return "", fmt.Errorf("%w: active_gateway contains invalid name %q: %v", ErrNoActiveGateway, name, err) - } - - return name, nil -} - -// listGatewayDirs returns a list of gateway directories found under the -// given base path. Each entry is just the directory name (gateway name). -func listGatewayDirs(base string) ([]string, error) { - gatewaysDir := filepath.Join(base, gatewaySubdir) - entries, err := os.ReadDir(gatewaysDir) - if err != nil { - if os.IsNotExist(err) { - return nil, nil - } - return nil, err - } - - var names []string - for _, e := range entries { - if e.IsDir() && validateGatewayName(e.Name()) == nil { - names = append(names, e.Name()) - } - } - return names, nil -} diff --git a/sdk/go/openshell/v1/gateway/paths_test.go b/sdk/go/openshell/v1/gateway/paths_test.go deleted file mode 100644 index fa3fb0d18a..0000000000 --- a/sdk/go/openshell/v1/gateway/paths_test.go +++ /dev/null @@ -1,374 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package gateway - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// --- T007: XDG resolution tests --- - -func TestUserConfigDir_XDGSet(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - - dir, err := userConfigDir() - require.NoError(t, err) - assert.Equal(t, filepath.Join(tmp, "openshell"), dir) -} - -func TestUserConfigDir_XDGUnset(t *testing.T) { - t.Setenv("XDG_CONFIG_HOME", "") - - dir, err := userConfigDir() - require.NoError(t, err) - - home, err := os.UserHomeDir() - require.NoError(t, err) - assert.Equal(t, filepath.Join(home, ".config", "openshell"), dir) -} - -func TestSystemGatewayDir(t *testing.T) { - dir := systemGatewayDir() - assert.Equal(t, filepath.FromSlash("/etc/openshell/gateways"), dir) -} - -func TestSystemGatewayDirOverride(t *testing.T) { - tmp := t.TempDir() - t.Setenv(systemGatewayDirEnv, tmp) - - assert.Equal(t, filepath.Join(tmp, "gateways"), systemGatewayDir()) -} - -func TestSystemGatewayDirIgnoresInvalidOverrides(t *testing.T) { - for _, override := range []string{"", "relative/path"} { - t.Run(override, func(t *testing.T) { - t.Setenv(systemGatewayDirEnv, override) - assert.Equal(t, filepath.FromSlash("/etc/openshell/gateways"), systemGatewayDir()) - }) - } -} - -func TestResolveGatewayDir_UserDir(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - - // Create a gateway directory in user config. - gwDir := filepath.Join(tmp, "openshell", "gateways", "prod") - require.NoError(t, os.MkdirAll(gwDir, 0o755)) - - dir, source, err := resolveGatewayDir("prod") - require.NoError(t, err) - assert.Equal(t, gwDir, dir) - assert.Equal(t, SourceUser, source) -} - -func TestResolveGatewayDir_NotFound(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - - _, _, err := resolveGatewayDir("nonexistent") - require.Error(t, err) - assert.ErrorIs(t, err, ErrGatewayNotFound) -} - -func TestResolveGatewayDir_InvalidName(t *testing.T) { - _, _, err := resolveGatewayDir("../etc") - require.Error(t, err) - assert.ErrorIs(t, err, ErrInvalidGatewayName) -} - -func TestResolveGatewayDir_UserPrecedenceOverSystem(t *testing.T) { - // This test verifies the search order: user dir is checked before - // system dir. We can only test the user dir path since we cannot - // write to /etc in tests. The logic is verified by the successful - // user dir resolution above. - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - - gwDir := filepath.Join(tmp, "openshell", "gateways", "shared") - require.NoError(t, os.MkdirAll(gwDir, 0o755)) - - dir, source, err := resolveGatewayDir("shared") - require.NoError(t, err) - assert.Equal(t, gwDir, dir) - assert.Equal(t, SourceUser, source) -} - -// --- T008: Name validation tests --- - -func TestValidateGatewayName_ValidNames(t *testing.T) { - validNames := []string{ - "prod", - "staging", - "my-gateway", - "gateway_1", - "PROD", - "a", - "test-gateway-01", - "A_B_C", - "gw.example.com", - "192.0.2.1", - "foo.bar", - "config.json", - "gw-1.example.com", - "COM10", - "LPT10", - "CONSOLE", - "com1x.example.com", - } - - for _, name := range validNames { - t.Run(name, func(t *testing.T) { - err := validateGatewayName(name) - assert.NoError(t, err, "expected %q to be valid", name) - }) - } -} - -func TestValidateGatewayName_Empty(t *testing.T) { - err := validateGatewayName("") - require.Error(t, err) - assert.ErrorIs(t, err, ErrInvalidGatewayName) - assert.Contains(t, err.Error(), "empty") -} - -func TestValidateGatewayName_PathSeparators(t *testing.T) { - cases := []string{ - "../etc", - "foo/bar", - "foo\\bar", - "/absolute", - } - - for _, name := range cases { - t.Run(name, func(t *testing.T) { - err := validateGatewayName(name) - require.Error(t, err) - assert.ErrorIs(t, err, ErrInvalidGatewayName) - }) - } -} - -func TestValidateGatewayName_DotComponents(t *testing.T) { - cases := []string{ - ".", - "..", - } - - for _, name := range cases { - t.Run(name, func(t *testing.T) { - err := validateGatewayName(name) - require.Error(t, err) - assert.ErrorIs(t, err, ErrInvalidGatewayName) - }) - } -} - -// --- T021: Active gateway resolution tests --- - -func TestResolveActiveGateway_ValidName(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - require.NoError(t, os.MkdirAll(filepath.Join(tmp, "openshell"), 0o755)) - require.NoError(t, os.WriteFile(filepath.Join(tmp, "openshell", "active_gateway"), []byte("my-gateway"), 0o644)) - - name, err := resolveActiveGateway() - require.NoError(t, err) - assert.Equal(t, "my-gateway", name) -} - -func TestResolveActiveGateway_WhitespaceHandling(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - require.NoError(t, os.MkdirAll(filepath.Join(tmp, "openshell"), 0o755)) - require.NoError(t, os.WriteFile(filepath.Join(tmp, "openshell", "active_gateway"), []byte(" my-gateway \n"), 0o644)) - - name, err := resolveActiveGateway() - require.NoError(t, err) - assert.Equal(t, "my-gateway", name) -} - -func TestResolveActiveGateway_FileMissing(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - t.Setenv(systemGatewayDirEnv, t.TempDir()) - require.NoError(t, os.MkdirAll(filepath.Join(tmp, "openshell"), 0o755)) - - _, err := resolveActiveGateway() - require.Error(t, err) - assert.ErrorIs(t, err, ErrNoActiveGateway) -} - -func TestResolveActiveGateway_EmptyFile(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - t.Setenv(systemGatewayDirEnv, t.TempDir()) - require.NoError(t, os.MkdirAll(filepath.Join(tmp, "openshell"), 0o755)) - require.NoError(t, os.WriteFile(filepath.Join(tmp, "openshell", "active_gateway"), []byte(" \n"), 0o644)) - - _, err := resolveActiveGateway() - require.Error(t, err) - assert.ErrorIs(t, err, ErrNoActiveGateway) -} - -// setActiveFiles points the user and system config dirs at temp dirs and -// writes active_gateway files when the given content is non-empty. -func setActiveFiles(t *testing.T, userContent, systemContent string) { - t.Helper() - user, system := t.TempDir(), t.TempDir() - t.Setenv("XDG_CONFIG_HOME", user) - t.Setenv(systemGatewayDirEnv, system) - if userContent != "" { - require.NoError(t, os.MkdirAll(filepath.Join(user, "openshell"), 0o755)) - writeFile(t, filepath.Join(user, "openshell"), "active_gateway", userContent) - } - if systemContent != "" { - writeFile(t, system, "active_gateway", systemContent) - } -} - -func TestResolveActiveGateway_FallsBackToSystem(t *testing.T) { - setActiveFiles(t, "", "corp\n") - - name, err := resolveActiveGateway() - require.NoError(t, err) - assert.Equal(t, "corp", name) -} - -func TestResolveActiveGateway_PrefersUser(t *testing.T) { - setActiveFiles(t, "mine", "corp") - - name, err := resolveActiveGateway() - require.NoError(t, err) - assert.Equal(t, "mine", name) -} - -func TestResolveActiveGateway_InvalidUserFallsBackToSystem(t *testing.T) { - setActiveFiles(t, "../escape", "corp") - - name, err := resolveActiveGateway() - require.NoError(t, err) - assert.Equal(t, "corp", name) -} - -func TestResolveActiveGateway_InvalidSystemIsIgnored(t *testing.T) { - setActiveFiles(t, "bad/user", "bad/system") - - _, err := resolveActiveGateway() - require.ErrorIs(t, err, ErrNoActiveGateway) - assert.Contains(t, err.Error(), "bad/user") - assert.NotContains(t, err.Error(), "bad/system") -} - -func TestResolveActiveGateway_UserDirErrorFallsBackToSystem(t *testing.T) { - setActiveFiles(t, "", "corp") - t.Setenv("XDG_CONFIG_HOME", "relative/path") - - name, err := resolveActiveGateway() - require.NoError(t, err) - assert.Equal(t, "corp", name) -} - -func TestResolveActiveGateway_UserDirErrorNoSystem(t *testing.T) { - setActiveFiles(t, "", "") - t.Setenv("XDG_CONFIG_HOME", "relative/path") - - _, err := resolveActiveGateway() - require.ErrorIs(t, err, ErrNoActiveGateway) - assert.Contains(t, err.Error(), "cannot determine config directory") -} - -func TestValidateGatewayName_SpecialCharacters(t *testing.T) { - cases := []struct { - name string - desc string - }{ - {"hello world", "space"}, - {"foo@bar", "at sign"}, - {"foo#bar", "hash"}, - {"café", "non-ASCII"}, - {"日本語", "unicode"}, - {"foo\tbar", "tab"}, - } - - for _, tc := range cases { - t.Run(tc.desc, func(t *testing.T) { - err := validateGatewayName(tc.name) - assert.ErrorIs(t, err, ErrInvalidGatewayName) - }) - } -} - -// Windows filename rules apply regardless of the host operating system. -func TestValidateGatewayName_WindowsFilenameRules(t *testing.T) { - invalid := []string{ - "CON", "con.txt", "CON.example.com", - "PRN", "prn.log", "AUX", "aux.json", "NUL", "nul.config", - "COM0", "COM1", "com9.txt", "COM1.example.com", - "LPT0", "LPT1", "lpt9.log", - "gateway.", ".hidden", "foo..bar", "gw.example.com.", - "gateway ", "foo bar", "foo:stream", "C:gateway", - "foo|bar", "foo?bar", "foo*bar", "foobar", "foo\"bar", - "gw\x01bar", "[2001:db8::1]", "2001:db8::1", - } - for _, name := range invalid { - t.Run(name, func(t *testing.T) { - err := validateGatewayName(name) - assert.ErrorIs(t, err, ErrInvalidGatewayName) - }) - } -} - -func TestHostDerivedGatewayNames(t *testing.T) { - tmp := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", tmp) - t.Setenv(systemGatewayDirEnv, filepath.Join(tmp, "system")) - - names := []string{"gw.example.com", "192.0.2.1"} - for _, name := range names { - dir := filepath.Join(tmp, "openshell", "gateways", name) - require.NoError(t, os.MkdirAll(dir, 0o755)) - require.NoError(t, os.WriteFile( - filepath.Join(dir, "metadata.json"), - []byte(`{"gateway_endpoint":"https://gw.example.com:443","auth_mode":"none"}`), - 0o600, - )) - - cfg, err := LoadConfig(name) - require.NoError(t, err) - assert.Equal(t, name, cfg.Name) - } - - require.NoError(t, os.WriteFile( - filepath.Join(tmp, "openshell", "active_gateway"), - []byte("gw.example.com"), 0o600, - )) - - active, err := LoadConfig("") - require.NoError(t, err) - assert.Equal(t, "gw.example.com", active.Name) - - gateways, err := ListGateways() - require.NoError(t, err) - got := make(map[string]bool) - for _, gw := range gateways { - got[gw.Name] = gw.Active - } - assert.Equal(t, map[string]bool{ - "gw.example.com": true, - "192.0.2.1": false, - }, got) -} - -func TestValidateGatewayName_NUL(t *testing.T) { - err := validateGatewayName("gw\x00example") - require.Error(t, err) - assert.ErrorIs(t, err, ErrInvalidGatewayName) -} diff --git a/sdk/go/openshell/v1/gateway/token.go b/sdk/go/openshell/v1/gateway/token.go deleted file mode 100644 index fea8d82f9a..0000000000 --- a/sdk/go/openshell/v1/gateway/token.go +++ /dev/null @@ -1,167 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package gateway - -import ( - "context" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - "sync" - "time" - - "golang.org/x/oauth2" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/oidctoken" -) - -const ( - // edgeTokenFile is the primary edge token filename. - edgeTokenFile = "edge_token" - - // cfTokenFile is the legacy Cloudflare token filename, used as - // fallback when edge_token does not exist. - cfTokenFile = "cf_token" -) - -// edgeTokenLoader provides lazy, thread-safe loading of the edge token -// from disk. Successful loads are cached; failures are retried so a token -// created after client construction can be picked up without a restart. -type edgeTokenLoader struct { - dir string - mu sync.Mutex - token string -} - -// load returns the edge token string, reading it from disk on the first -// call. It tries edge_token first, falling back to cf_token for legacy -// compatibility. Only a successful result is cached. -func (l *edgeTokenLoader) load() (string, error) { - l.mu.Lock() - defer l.mu.Unlock() - if l.token != "" { - return l.token, nil - } - token, err := readEdgeToken(l.dir) - if err != nil { - return "", err - } - l.token = token - return token, nil -} - -// readEdgeToken reads the edge token from the given directory. It tries -// edge_token first, then cf_token as a legacy fallback. The file content -// is trimmed of surrounding whitespace. -func readEdgeToken(dir string) (string, error) { - // Try primary edge_token file. - primary := filepath.Join(dir, edgeTokenFile) - data, err := os.ReadFile(primary) - if err == nil { - token := strings.TrimSpace(string(data)) - if token == "" { - return "", fmt.Errorf("%w: edge_token file is empty", ErrTokenLoad) - } - return token, nil - } - if !os.IsNotExist(err) { - return "", fmt.Errorf("%w: cannot read %s: %v", ErrTokenLoad, edgeTokenFile, err) - } - - // Fallback to legacy cf_token file (only when edge_token is absent). - legacy := filepath.Join(dir, cfTokenFile) - data, err = os.ReadFile(legacy) - if err != nil { - return "", fmt.Errorf("%w: neither edge_token nor cf_token found in gateway directory", ErrTokenLoad) - } - - token := strings.TrimSpace(string(data)) - if token == "" { - return "", fmt.Errorf("%w: cf_token file is empty", ErrTokenLoad) - } - - return token, nil -} - -// diskTokenSource implements oauth2.TokenSource by reading oidc_token.json -// from disk on every Token() call. This allows the source to pick up -// tokens refreshed by the Rust CLI without process restart. -type diskTokenSource struct { - dir string - name string -} - -// newDiskTokenSource returns an oauth2.TokenSource that reads -// oidc_token.json from the directory of the named gateway on each -// Token() call. -func newDiskTokenSource(dir, name string) oauth2.TokenSource { - return &diskTokenSource{dir: dir, name: name} -} - -// Token reads and parses oidc_token.json, returning an oauth2.Token. -// The file is read on every call to pick up CLI-refreshed tokens. -func (d *diskTokenSource) Token() (*oauth2.Token, error) { - data, err := os.ReadFile(filepath.Join(d.dir, oidctoken.FileName)) - if err != nil { - return nil, d.loadError("cannot read %s: %v", oidctoken.FileName, err) - } - - var bundle oidctoken.Bundle - if err := json.Unmarshal(data, &bundle); err != nil { - return nil, d.loadError("invalid JSON in %s", oidctoken.FileName) - } - - if bundle.AccessToken == "" { - return nil, d.loadError("missing access_token in %s", oidctoken.FileName) - } - // Files from earlier Go SDK builds lack these fields and expires_at; - // accepting them would treat their tokens as never expiring. - if bundle.Issuer == "" || bundle.ClientID == "" { - return nil, d.loadError("OIDC token for gateway %q is missing issuer or client_id", d.name) - } - - tok := bundle.Token() - if !tok.Expiry.IsZero() && !time.Now().Before(tok.Expiry) { - return nil, d.loadError("OIDC token for gateway %q expired at %s", - d.name, tok.Expiry.UTC().Format(time.RFC3339)) - } - - return tok, nil -} - -// loadError wraps ErrTokenLoad with the CLI login command that repairs -// the token file. -func (d *diskTokenSource) loadError(format string, args ...any) error { - return fmt.Errorf("%w: %s; run `openshell gateway login %s`", - ErrTokenLoad, fmt.Sprintf(format, args...), d.name) -} - -// lazyEdgeAuth implements types.AuthProvider for the cloudflare_jwt auth -// mode. Token loading is deferred to GetRequestMetadata so that -// NewClient succeeds even when the token file is missing on disk (FR-007). -// The error surfaces on the first authentication attempt. -type lazyEdgeAuth struct { - loader *edgeTokenLoader -} - -// GetRequestMetadata loads the edge token lazily and returns it as a -// Bearer authorization header. The first load is cached by the -// underlying edgeTokenLoader. -func (a *lazyEdgeAuth) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) { - token, err := a.loader.load() - if err != nil { - return nil, err - } - return map[string]string{ - "authorization": "Bearer " + token, - }, nil -} - -// RequireTransportSecurity returns true because Bearer tokens must not -// be sent over plaintext connections. -func (a *lazyEdgeAuth) RequireTransportSecurity() bool { - return true -} diff --git a/sdk/go/openshell/v1/gateway/token_test.go b/sdk/go/openshell/v1/gateway/token_test.go deleted file mode 100644 index 19a8f1febc..0000000000 --- a/sdk/go/openshell/v1/gateway/token_test.go +++ /dev/null @@ -1,271 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package gateway - -import ( - "fmt" - "os" - "path/filepath" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/oidctoken" -) - -// --- T014: Edge token loading tests --- - -func TestReadEdgeToken_PrimaryFile(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, edgeTokenFile, "my-edge-token-123") - - token, err := readEdgeToken(dir) - require.NoError(t, err) - assert.Equal(t, "my-edge-token-123", token) -} - -func TestReadEdgeToken_PrimaryFileWithWhitespace(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, edgeTokenFile, " token-with-whitespace \n") - - token, err := readEdgeToken(dir) - require.NoError(t, err) - assert.Equal(t, "token-with-whitespace", token) -} - -func TestReadEdgeToken_CfTokenFallback(t *testing.T) { - dir := t.TempDir() - // No edge_token file, only cf_token. - writeFile(t, dir, cfTokenFile, "legacy-cf-token") - - token, err := readEdgeToken(dir) - require.NoError(t, err) - assert.Equal(t, "legacy-cf-token", token) -} - -func TestReadEdgeToken_PrimaryTakesPrecedence(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, edgeTokenFile, "primary-token") - writeFile(t, dir, cfTokenFile, "legacy-token") - - token, err := readEdgeToken(dir) - require.NoError(t, err) - assert.Equal(t, "primary-token", token) -} - -func TestReadEdgeToken_MissingBothFiles(t *testing.T) { - dir := t.TempDir() - - _, err := readEdgeToken(dir) - require.Error(t, err) - assert.ErrorIs(t, err, ErrTokenLoad) - assert.Contains(t, err.Error(), "neither edge_token nor cf_token") -} - -func TestReadEdgeToken_EmptyPrimaryFile(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, edgeTokenFile, " \n") - - _, err := readEdgeToken(dir) - require.Error(t, err) - assert.ErrorIs(t, err, ErrTokenLoad) - assert.Contains(t, err.Error(), "empty") -} - -func TestReadEdgeToken_EmptyFallbackFile(t *testing.T) { - dir := t.TempDir() - // No edge_token, only empty cf_token. - writeFile(t, dir, cfTokenFile, "") - - _, err := readEdgeToken(dir) - require.Error(t, err) - assert.ErrorIs(t, err, ErrTokenLoad) - assert.Contains(t, err.Error(), "empty") -} - -func TestEdgeTokenLoader_Lazy(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, edgeTokenFile, "lazy-token") - - loader := &edgeTokenLoader{dir: dir} - - // First call reads from disk. - token1, err := loader.load() - require.NoError(t, err) - assert.Equal(t, "lazy-token", token1) - - // Remove the file. Second call should return cached value. - require.NoError(t, os.Remove(filepath.Join(dir, edgeTokenFile))) - - token2, err := loader.load() - require.NoError(t, err) - assert.Equal(t, "lazy-token", token2, "should return cached token") -} - -func TestEdgeTokenLoader_LazyError(t *testing.T) { - dir := t.TempDir() - // No token files exist. - - loader := &edgeTokenLoader{dir: dir} - - // First call fails. - _, err1 := loader.load() - require.Error(t, err1) - - // Write the file now. A transient load failure must not be cached. - writeFile(t, dir, edgeTokenFile, "late-token") - - token, err2 := loader.load() - require.NoError(t, err2) - assert.Equal(t, "late-token", token) -} - -// --- T015: diskTokenSource tests --- - -const loginHint = "run `openshell gateway login prod`" - -func TestDiskTokenSource_ValidBundle(t *testing.T) { - dir := t.TempDir() - fixture, err := os.ReadFile(filepath.Join("..", "..", "..", "..", "conformance", "oidc-token-bundle.json")) - require.NoError(t, err) - writeFile(t, dir, oidctoken.FileName, string(fixture)) - - src := newDiskTokenSource(dir, "prod") - tok, err := src.Token() - require.NoError(t, err) - assert.Equal(t, "conformance-access-token", tok.AccessToken) - assert.Equal(t, "conformance-refresh-token", tok.RefreshToken) - assert.Equal(t, "Bearer", tok.TokenType) - assert.Equal(t, time.Unix(4102444800, 0), tok.Expiry) -} - -func TestDiskTokenSource_NoExpiry(t *testing.T) { - for _, expiresAt := range []string{"", "null"} { - dir := t.TempDir() - writeFile(t, dir, oidctoken.FileName, oidcBundleJSON("no-expiry-token", expiresAt)) - - src := newDiskTokenSource(dir, "prod") - tok, err := src.Token() - require.NoError(t, err, "expires_at=%q", expiresAt) - assert.Equal(t, "no-expiry-token", tok.AccessToken) - assert.True(t, tok.Expiry.IsZero(), "should have zero expiry for expires_at=%q", expiresAt) - } -} - -func TestDiskTokenSource_ExpiredToken(t *testing.T) { - for _, expiresAt := range []string{fmt.Sprint(time.Now().Add(-time.Hour).Unix()), "0"} { - dir := t.TempDir() - writeFile(t, dir, oidctoken.FileName, oidcBundleJSON("expired-token", expiresAt)) - - src := newDiskTokenSource(dir, "prod") - _, err := src.Token() - require.ErrorIs(t, err, ErrTokenLoad, "expires_at=%s", expiresAt) - assert.Contains(t, err.Error(), "expired at") - assert.Contains(t, err.Error(), loginHint) - } -} - -func TestDiskTokenSource_MissingIssuerOrClientID(t *testing.T) { - expiry := time.Now().Add(time.Hour).UTC().Format(time.RFC3339) - bundles := map[string]string{ - "earlier Go SDK format": `{"access_token": "a", "refresh_token": "r", "expiry": "` + expiry + `", "expires_in": 3600}`, - "no issuer": `{"access_token": "a", "client_id": "openshell-cli"}`, - "no client_id": `{"access_token": "a", "issuer": "https://issuer.example.com"}`, - "empty issuer": `{"access_token": "a", "issuer": "", "client_id": "openshell-cli"}`, - } - for name, content := range bundles { - t.Run(name, func(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, oidctoken.FileName, content) - - src := newDiskTokenSource(dir, "prod") - _, err := src.Token() - require.Error(t, err) - assert.ErrorIs(t, err, ErrTokenLoad) - assert.Contains(t, err.Error(), `OIDC token for gateway "prod" is missing issuer or client_id`) - assert.Contains(t, err.Error(), loginHint) - }) - } -} - -func TestDiskTokenSource_MissingFile(t *testing.T) { - dir := t.TempDir() - - src := newDiskTokenSource(dir, "prod") - _, err := src.Token() - require.Error(t, err) - assert.ErrorIs(t, err, ErrTokenLoad) - assert.Contains(t, err.Error(), loginHint) -} - -func TestDiskTokenSource_MalformedJSON(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, oidctoken.FileName, `{not valid json}`) - - src := newDiskTokenSource(dir, "prod") - _, err := src.Token() - require.Error(t, err) - assert.ErrorIs(t, err, ErrTokenLoad) - assert.Contains(t, err.Error(), "invalid JSON") - assert.Contains(t, err.Error(), loginHint) -} - -func TestDiskTokenSource_MissingAccessToken(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, oidctoken.FileName, `{"refresh_token": "only-refresh"}`) - - src := newDiskTokenSource(dir, "prod") - _, err := src.Token() - require.Error(t, err) - assert.ErrorIs(t, err, ErrTokenLoad) - assert.Contains(t, err.Error(), "missing access_token") -} - -func TestDiskTokenSource_ReReadsOnEachCall(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, oidctoken.FileName, oidcBundleJSON("token-v1", "")) - - src := newDiskTokenSource(dir, "prod") - tok1, err := src.Token() - require.NoError(t, err) - assert.Equal(t, "token-v1", tok1.AccessToken) - - // Update the file. Next call should read the new value. - writeFile(t, dir, oidctoken.FileName, oidcBundleJSON("token-v2", "")) - - tok2, err := src.Token() - require.NoError(t, err) - assert.Equal(t, "token-v2", tok2.AccessToken) -} - -func TestDiskTokenSource_InvalidExpiryFormat(t *testing.T) { - dir := t.TempDir() - writeFile(t, dir, oidctoken.FileName, oidcBundleJSON("token-xyz", "-1")) - - src := newDiskTokenSource(dir, "prod") - _, err := src.Token() - require.Error(t, err) - assert.ErrorIs(t, err, ErrTokenLoad) - assert.Contains(t, err.Error(), "invalid JSON") -} - -// writeFile is a test helper that writes a file with the given content. -func writeFile(t *testing.T, dir, name, content string) { - t.Helper() - err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644) - require.NoError(t, err) -} - -// oidcBundleJSON returns a CLI-format oidc_token.json body. expiresAt is -// the raw JSON value of expires_at; an empty string omits the field. -func oidcBundleJSON(accessToken, expiresAt string) string { - expiry := "" - if expiresAt != "" { - expiry = `"expires_at": ` + expiresAt + `, ` - } - return `{"access_token": "` + accessToken + `", ` + expiry + - `"issuer": "https://issuer.example.com", "client_id": "openshell-cli"}` -} diff --git a/sdk/go/openshell/v1/health.go b/sdk/go/openshell/v1/health.go deleted file mode 100644 index ac05337460..0000000000 --- a/sdk/go/openshell/v1/health.go +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// HealthResult holds the result of a health check. -type HealthResult = types.HealthResult - -// GatewayInfo holds operational metadata about the gateway. -type GatewayInfo = types.GatewayInfo - -// ComputeDriverInfo describes a compute backend available on the gateway. -type ComputeDriverInfo = types.ComputeDriverInfo - -// ExtensionInfo describes one successful gateway/extension negotiation. -type ExtensionInfo = types.ExtensionInfo - -// ExtensionKind identifies one supported extension family. -type ExtensionKind = types.ExtensionKind - -// ExtensionKind constants re-exported from the types package. -const ( - ExtensionKindComputeDriver = types.ExtensionKindComputeDriver - ExtensionKindCredentialDriver = types.ExtensionKindCredentialDriver - ExtensionKindGatewayInterceptor = types.ExtensionKindGatewayInterceptor - ExtensionKindSupervisorMiddleware = types.ExtensionKindSupervisorMiddleware - ExtensionKindUnknown = types.ExtensionKindUnknown -) - -// ServiceStatus describes the health state of the gateway. -type ServiceStatus = types.ServiceStatus - -// ServiceStatus constants. -const ( - ServiceStatusHealthy = types.ServiceStatusHealthy - ServiceStatusDegraded = types.ServiceStatusDegraded - ServiceStatusUnhealthy = types.ServiceStatusUnhealthy - ServiceStatusUnknown = types.ServiceStatusUnknown -) - -// CurrentUser holds the authenticated caller's identity. -type CurrentUser = types.CurrentUser - -// HealthInterface defines health check and gateway info operations. -type HealthInterface interface { - Check(ctx context.Context) (*HealthResult, error) - GetGatewayInfo(ctx context.Context) (*GatewayInfo, error) - GetCurrentUser(ctx context.Context) (*CurrentUser, error) -} diff --git a/sdk/go/openshell/v1/health_client.go b/sdk/go/openshell/v1/health_client.go deleted file mode 100644 index 87c958a0dc..0000000000 --- a/sdk/go/openshell/v1/health_client.go +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" -) - -type healthClient struct { - client pb.OpenShellClient -} - -func newHealthClient(conn grpc.ClientConnInterface) *healthClient { - return &healthClient{client: pb.NewOpenShellClient(conn)} -} - -func (h *healthClient) Check(ctx context.Context) (*HealthResult, error) { - resp, err := h.client.Health(ctx, &pb.HealthRequest{}) - if err != nil { - return nil, converter.FromGRPCError(err) - } - - return &HealthResult{ - Healthy: resp.GetStatus() == pb.ServiceStatus_SERVICE_STATUS_HEALTHY, - Version: resp.GetVersion(), - }, nil -} - -func (h *healthClient) GetGatewayInfo(ctx context.Context) (*GatewayInfo, error) { - resp, err := h.client.GetGatewayInfo(ctx, &pb.GetGatewayInfoRequest{}) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.GatewayInfoFromProto(resp), nil -} - -func (h *healthClient) GetCurrentUser(ctx context.Context) (*CurrentUser, error) { - resp, err := h.client.GetCurrentUser(ctx, &pb.GetCurrentUserRequest{}) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.CurrentUserFromProto(resp), nil -} diff --git a/sdk/go/openshell/v1/health_client_test.go b/sdk/go/openshell/v1/health_client_test.go deleted file mode 100644 index dc82fe1e4e..0000000000 --- a/sdk/go/openshell/v1/health_client_test.go +++ /dev/null @@ -1,271 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "net" - "testing" - - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" -) - -const bufSize = 1024 * 1024 - -type mockHealthServer struct { - pb.UnimplementedOpenShellServer - status pb.ServiceStatus - version string - err error - gatewayInfoResp *pb.GetGatewayInfoResponse - currentUserResp *pb.GetCurrentUserResponse - gatewayInfoErr error - currentUserErr error -} - -func TestExtensionKindConstantsAreReexported(t *testing.T) { - assert.Equal(t, ExtensionKind("ComputeDriver"), ExtensionKindComputeDriver) - assert.Equal(t, ExtensionKind("CredentialDriver"), ExtensionKindCredentialDriver) - assert.Equal(t, ExtensionKind("GatewayInterceptor"), ExtensionKindGatewayInterceptor) - assert.Equal(t, ExtensionKind("SupervisorMiddleware"), ExtensionKindSupervisorMiddleware) - assert.Equal(t, ExtensionKind("Unknown"), ExtensionKindUnknown) -} - -func (s *mockHealthServer) Health(_ context.Context, _ *pb.HealthRequest) (*pb.HealthResponse, error) { - if s.err != nil { - return nil, s.err - } - return &pb.HealthResponse{ - Status: s.status, - Version: s.version, - }, nil -} - -func (s *mockHealthServer) GetGatewayInfo(_ context.Context, _ *pb.GetGatewayInfoRequest) (*pb.GetGatewayInfoResponse, error) { - if s.gatewayInfoErr != nil { - return nil, s.gatewayInfoErr - } - return s.gatewayInfoResp, nil -} - -func (s *mockHealthServer) GetCurrentUser(_ context.Context, _ *pb.GetCurrentUserRequest) (*pb.GetCurrentUserResponse, error) { - if s.currentUserErr != nil { - return nil, s.currentUserErr - } - return s.currentUserResp, nil -} - -func newMockHealthServer(s pb.ServiceStatus, version string, err error) (*grpc.ClientConn, func()) { - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, &mockHealthServer{status: s, version: version, err: err}) - - go func() { _ = srv.Serve(lis) }() - - conn, err2 := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - - if err2 != nil { - srv.Stop() - panic("grpc.NewClient failed: " + err2.Error()) - } - - return conn, func() { - _ = conn.Close() - srv.Stop() - } -} - -func TestHealthCheck_Success(t *testing.T) { - conn, cleanup := newMockHealthServer(pb.ServiceStatus_SERVICE_STATUS_HEALTHY, "1.2.3", nil) - defer cleanup() - - h := newHealthClient(conn) - result, err := h.Check(context.Background()) - - require.NoError(t, err) - assert.True(t, result.Healthy) - assert.Equal(t, "1.2.3", result.Version) -} - -func TestHealthCheck_Degraded(t *testing.T) { - conn, cleanup := newMockHealthServer(pb.ServiceStatus_SERVICE_STATUS_DEGRADED, "2.0.0", nil) - defer cleanup() - - h := newHealthClient(conn) - result, err := h.Check(context.Background()) - - require.NoError(t, err) - assert.False(t, result.Healthy) - assert.Equal(t, "2.0.0", result.Version) -} - -func TestHealthCheck_Unhealthy(t *testing.T) { - conn, cleanup := newMockHealthServer(pb.ServiceStatus_SERVICE_STATUS_UNHEALTHY, "3.0.0", nil) - defer cleanup() - - h := newHealthClient(conn) - result, err := h.Check(context.Background()) - - require.NoError(t, err) - assert.False(t, result.Healthy) - assert.Equal(t, "3.0.0", result.Version) -} - -func TestHealthCheck_Unavailable(t *testing.T) { - conn, cleanup := newMockHealthServer(0, "", status.Error(codes.Unavailable, "service down")) - defer cleanup() - - h := newHealthClient(conn) - _, err := h.Check(context.Background()) - - require.Error(t, err) - assert.True(t, IsUnavailable(err)) -} - -func newMockGatewayInfoServer(resp *pb.GetGatewayInfoResponse, err error) (*grpc.ClientConn, func()) { - mock := &mockHealthServer{ - gatewayInfoResp: resp, - gatewayInfoErr: err, - status: pb.ServiceStatus_SERVICE_STATUS_HEALTHY, - version: "1.0.0", - } - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - - go func() { _ = srv.Serve(lis) }() - - conn, err2 := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - if err2 != nil { - srv.Stop() - panic("grpc.NewClient failed: " + err2.Error()) - } - - return conn, func() { - _ = conn.Close() - srv.Stop() - } -} - -func TestGetGatewayInfo_Success(t *testing.T) { - resp := &pb.GetGatewayInfoResponse{ - Status: pb.ServiceStatus_SERVICE_STATUS_HEALTHY, - GatewayVersion: "1.5.0", - ComputeDrivers: []*pb.ComputeDriverInfo{ - { - Name: "k8s", - Capabilities: &pb.ComputeDriverCapabilities{ - DriverName: "kubernetes", - DriverVersion: "2.1.0", - }, - }, - }, - } - conn, cleanup := newMockGatewayInfoServer(resp, nil) - defer cleanup() - - h := newHealthClient(conn) - info, err := h.GetGatewayInfo(context.Background()) - - require.NoError(t, err) - require.NotNil(t, info) - assert.Equal(t, ServiceStatusHealthy, info.Status) - assert.Equal(t, "1.5.0", info.Version) - require.Len(t, info.ComputeDrivers, 1) - assert.Equal(t, "k8s", info.ComputeDrivers[0].Name) - assert.Equal(t, "kubernetes", info.ComputeDrivers[0].DriverName) - assert.Equal(t, "2.1.0", info.ComputeDrivers[0].DriverVersion) -} - -func TestGetGatewayInfo_Error(t *testing.T) { - conn, cleanup := newMockGatewayInfoServer(nil, status.Error(codes.PermissionDenied, "not admin")) - defer cleanup() - - h := newHealthClient(conn) - _, err := h.GetGatewayInfo(context.Background()) - - require.Error(t, err) - assert.True(t, IsPermissionDenied(err)) -} - -func newMockCurrentUserServer(resp *pb.GetCurrentUserResponse, err error) (*grpc.ClientConn, func()) { - mock := &mockHealthServer{ - currentUserResp: resp, - currentUserErr: err, - status: pb.ServiceStatus_SERVICE_STATUS_HEALTHY, - version: "1.0.0", - } - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - - go func() { _ = srv.Serve(lis) }() - - conn, err2 := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - if err2 != nil { - srv.Stop() - panic("grpc.NewClient failed: " + err2.Error()) - } - - return conn, func() { - _ = conn.Close() - srv.Stop() - } -} - -func TestGetCurrentUser_Success(t *testing.T) { - resp := &pb.GetCurrentUserResponse{ - Subject: "user-123", - DisplayName: "Test User", - Roles: []string{"admin"}, - Scopes: []string{"read", "write"}, - IdentityProvider: "oidc-provider", - } - conn, cleanup := newMockCurrentUserServer(resp, nil) - defer cleanup() - - h := newHealthClient(conn) - user, err := h.GetCurrentUser(context.Background()) - - require.NoError(t, err) - require.NotNil(t, user) - assert.Equal(t, "user-123", user.Subject) - assert.Equal(t, "Test User", user.DisplayName) - assert.Equal(t, []string{"admin"}, user.Roles) - assert.Equal(t, []string{"read", "write"}, user.Scopes) - assert.Equal(t, "oidc-provider", user.IdentityProvider) -} - -func TestGetCurrentUser_Unauthenticated(t *testing.T) { - conn, cleanup := newMockCurrentUserServer(nil, status.Error(codes.Unauthenticated, "invalid token")) - defer cleanup() - - h := newHealthClient(conn) - _, err := h.GetCurrentUser(context.Background()) - - require.Error(t, err) - assert.True(t, IsUnauthenticated(err)) -} diff --git a/sdk/go/openshell/v1/integration_test.go b/sdk/go/openshell/v1/integration_test.go deleted file mode 100644 index a9328deff3..0000000000 --- a/sdk/go/openshell/v1/integration_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -//go:build integration - -package v1 - -import ( - "context" - "errors" - "fmt" - "os" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func gatewayAddress(t *testing.T) string { - t.Helper() - addr := os.Getenv("OPENSHELL_GATEWAY_ADDRESS") - if addr == "" { - t.Skip("OPENSHELL_GATEWAY_ADDRESS not set") - } - return addr -} - -func TestIntegration_HealthCheck(t *testing.T) { - addr := gatewayAddress(t) - - client, err := NewClient(Config{Address: addr}) - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, client.Close()) }) - - _, err = client.Health().Check(context.Background()) - require.NoError(t, err) -} - -func TestIntegration_SandboxExecSmoke(t *testing.T) { - addr := gatewayAddress(t) - client, err := NewClient(Config{Address: addr}) - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, client.Close()) }) - - image := os.Getenv("OPENSHELL_GO_SDK_TEST_IMAGE") - if image == "" { - image = "nvcr.io/nvidia/base/ubuntu:24.04" - } - name := fmt.Sprintf("go-smoke-%09d", time.Now().UnixNano()%1_000_000_000) - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - _, err = client.Sandboxes().Create(ctx, "default", name, &SandboxSpec{ - Template: &SandboxTemplate{Image: image}, - }) - require.NoError(t, err) - t.Cleanup(func() { - cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cleanupCancel() - require.NoError(t, client.Sandboxes().Delete(cleanupCtx, "default", name)) - }) - - _, err = client.Sandboxes().WaitReady(ctx, "default", name) - require.NoError(t, err) - result, err := client.Exec().Run(ctx, "default", name, - []string{"sh", "-c", "printf openshell-go-sdk-smoke"}, ExecOptions{}) - require.NoError(t, err) - require.Equal(t, 0, result.ExitCode) - require.Equal(t, "openshell-go-sdk-smoke", string(result.Stdout)) - require.Empty(t, result.Stderr) -} - -func TestIntegration_FileTransfer(t *testing.T) { - addr := gatewayAddress(t) - - client, err := NewClient(Config{Address: addr}) - require.NoError(t, err) - defer client.Close() - - err = client.Files().Upload(context.Background(), "default", "unused", "missing", "/tmp/missing") - require.Error(t, err) - require.True(t, errors.Is(err, ErrTransportNotAvailable)) -} diff --git a/sdk/go/openshell/v1/internal/converter/copy.go b/sdk/go/openshell/v1/internal/converter/copy.go deleted file mode 100644 index dc98588fdd..0000000000 --- a/sdk/go/openshell/v1/internal/converter/copy.go +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -// CopyStringMap returns a shallow copy of a string-to-string map. -// Returns nil for nil input. -func CopyStringMap(m map[string]string) map[string]string { - if m == nil { - return nil - } - c := make(map[string]string, len(m)) - for k, v := range m { - c[k] = v - } - return c -} - -// CopyBoolPtr returns a copy of a *bool pointer. -// Returns nil for nil input. -func CopyBoolPtr(p *bool) *bool { - if p == nil { - return nil - } - v := *p - return &v -} - -// CopyInt32Ptr returns a copy of an *int32 pointer. -func CopyInt32Ptr(p *int32) *int32 { - if p == nil { - return nil - } - v := *p - return &v -} - -// CopyStringSlice returns a copy of a string slice. -// Returns nil for nil input. -func CopyStringSlice(s []string) []string { - if s == nil { - return nil - } - c := make([]string, len(s)) - copy(c, s) - return c -} - -// CopyByteSlice returns a copy of a byte slice. -// Returns nil for nil input. -func CopyByteSlice(b []byte) []byte { - if b == nil { - return nil - } - c := make([]byte, len(b)) - copy(c, b) - return c -} - -func boolCount(flags ...bool) int { - n := 0 - for _, f := range flags { - if f { - n++ - } - } - return n -} diff --git a/sdk/go/openshell/v1/internal/converter/coverage_test.go b/sdk/go/openshell/v1/internal/converter/coverage_test.go deleted file mode 100644 index 8a457d0014..0000000000 --- a/sdk/go/openshell/v1/internal/converter/coverage_test.go +++ /dev/null @@ -1,464 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - sandboxpb "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - "google.golang.org/protobuf/reflect/protoreflect" -) - -// These tests use protobuf reflection to detect proto fields that the -// converter layer does not handle. When buf generates new fields from an -// updated .proto, the field name appears in the proto descriptor but not in -// the "handled" set below. -// -// Unhandled fields FAIL the test so that proto drift is caught immediately. -// If a field is intentionally deferred, add it to the "skipped" set with a -// justification comment. - -func TestConverterCoversAllProtoFields_SandboxSpec(t *testing.T) { - handled := fieldSet{ - "log_level": true, - "environment": true, - "template": true, - "policy": true, - "providers": true, - "resource_requirements": true, - "command": true, - "tty": true, - "restart_policy": true, - } - - // The gateway owns this identity. Provider status exposes it through the - // raw API; callers must not supply it when constructing a sandbox spec. - skipped := fieldSet{ - "provider_attachment_epoch": true, - } - assertAllFieldsCovered(t, (&pb.SandboxSpec{}).ProtoReflect().Descriptor(), handled, skipped) -} - -func TestConverterCoversAllProtoFields_SandboxTemplate(t *testing.T) { - handled := fieldSet{ - "image": true, - "runtime_class_name": true, - "agent_socket": true, - "labels": true, - "annotations": true, - "environment": true, - "resources": true, - "user_namespaces": true, - "driver_config": true, - } - - assertAllFieldsCovered(t, (&pb.SandboxTemplate{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_SandboxWorkloadTemplate(t *testing.T) { - handled := fieldSet{ - "metadata": true, - "spec": true, - } - - assertAllFieldsCovered(t, (&pb.SandboxWorkloadTemplate{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_SandboxWorkloadTemplateSpec(t *testing.T) { - handled := fieldSet{ - "workload": true, - "driver_config": true, - "desired_service_level": true, - } - - assertAllFieldsCovered(t, (&pb.SandboxWorkloadTemplateSpec{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_SandboxWorkloadConfig(t *testing.T) { - handled := fieldSet{ - "image": true, - "environment": true, - "resources": true, - } - - assertAllFieldsCovered(t, (&pb.SandboxWorkloadConfig{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_SandboxResources(t *testing.T) { - handled := fieldSet{ - "cpu": true, - "memory": true, - "gpu": true, - } - - assertAllFieldsCovered(t, (&pb.SandboxResources{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_SandboxServiceLevel(t *testing.T) { - handled := fieldSet{ - "startup": true, - } - - assertAllFieldsCovered(t, (&pb.SandboxServiceLevel{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_SandboxStartup(t *testing.T) { - handled := fieldSet{ - "ready_within": true, - "max_burst": true, - } - - assertAllFieldsCovered(t, (&pb.SandboxStartup{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_SandboxStatus(t *testing.T) { - handled := fieldSet{ - "agent_pod": true, - "agent_fd": true, - "sandbox_fd": true, - "phase": true, - "conditions": true, - "endpoint_statuses": true, - "last_token_grant_failure": true, - "current_policy_version": true, - "exit_code": true, - "configuration_admission": true, - "restart_count": true, - "next_restart_time": true, - "main_process_started_time": true, - } - // The instance ID coordinates internal gateway/supervisor lifecycle - // fencing. The first-activation marker governs static policy repair. - // Provisioning carries gateway-owned attempt, deadline, and cleanup state; - // the curated API exposes its outcome through phase and conditions. Detailed - // lifecycle bookkeeping remains available through the raw protobuf API. - // Image updates are raw-only; their pending reconciliation intent and - // monotonic image revision bookkeeping are not part of the curated status model. - skipped := fieldSet{"main_process_instance_id": true, "configuration_activated": true, "provisioning": true, "image_update": true, "image_revision": true} - - assertAllFieldsCovered(t, (&pb.SandboxStatus{}).ProtoReflect().Descriptor(), handled, skipped) -} - -func TestConverterCoversAllProtoFields_SandboxCondition(t *testing.T) { - handled := fieldSet{ - "type": true, - "status": true, - "reason": true, - "message": true, - "transition_time": true, - } - - assertAllFieldsCovered(t, (&pb.SandboxCondition{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_EndpointStatus(t *testing.T) { - handled := fieldSet{ - "endpoint_id": true, - "host": true, - "ports": true, - "path": true, - "last_result": true, - "last_reported_time": true, - "last_failure": true, - } - - assertAllFieldsCovered(t, (&pb.EndpointStatus{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_EndpointFailure(t *testing.T) { - handled := fieldSet{ - "domain": true, - "code": true, - "recovery_action": true, - "provider": true, - "credential_key": true, - "stage": true, - "upstream_http_status": true, - "request_id": true, - } - - assertAllFieldsCovered(t, (&pb.EndpointFailure{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_TokenGrantFailureObservation(t *testing.T) { - handled := fieldSet{ - "destination_host": true, - "destination_port": true, - "failure": true, - } - assertAllFieldsCovered(t, (&pb.TokenGrantFailureObservation{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_SandboxPolicy(t *testing.T) { - handled := fieldSet{ - "version": true, - "filesystem": true, - "network_policies": true, - "process": true, - "landlock": true, - "network_middlewares": true, - } - - assertAllFieldsCovered(t, (&sandboxpb.SandboxPolicy{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_NetworkMiddlewareConfig(t *testing.T) { - handled := fieldSet{ - "name": true, - "middleware": true, - "config": true, - "on_error": true, - "endpoints": true, - "order": true, - } - - assertAllFieldsCovered(t, (&sandboxpb.NetworkMiddlewareConfig{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_MiddlewareEndpointSelector(t *testing.T) { - handled := fieldSet{ - "include": true, - "exclude": true, - } - - assertAllFieldsCovered(t, (&sandboxpb.MiddlewareEndpointSelector{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_NetworkEndpoint(t *testing.T) { - handled := fieldSet{ - "host": true, - "port": true, - "ports": true, - "protocol": true, - "tls": true, - "enforcement": true, - "access": true, - "rules": true, - "allowed_ips": true, - "deny_rules": true, - "allow_encoded_slash": true, - "persisted_queries": true, - "graphql_persisted_queries": true, - "graphql_max_body_bytes": true, - "path": true, - "websocket_credential_rewrite": true, - "request_body_credential_rewrite": true, - "allow_uninspected_credentials": true, - "provider_credentialed": true, - "advisor_proposed": true, - "credential_signing": true, - "signing_service": true, - "signing_region": true, - "json_rpc_max_body_bytes": true, - "mcp": true, - "credential_binding": true, - } - - // The gateway derives this authority from the effective policy. It is - // available through the raw API, not authored SDK endpoint settings. - skipped := fieldSet{ - "token_grant_owner": true, - } - assertAllFieldsCovered(t, (&sandboxpb.NetworkEndpoint{}).ProtoReflect().Descriptor(), handled, skipped) -} - -func TestConverterCoversAllProtoFields_L7Allow(t *testing.T) { - handled := fieldSet{ - "method": true, - "path": true, - "command": true, - "query": true, - "operation_type": true, - "operation_name": true, - "fields": true, - "params": true, - } - - assertAllFieldsCovered(t, (&sandboxpb.L7Allow{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_L7DenyRule(t *testing.T) { - handled := fieldSet{ - "method": true, - "path": true, - "command": true, - "query": true, - "operation_type": true, - "operation_name": true, - "fields": true, - "params": true, - } - - assertAllFieldsCovered(t, (&sandboxpb.L7DenyRule{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_Provider(t *testing.T) { - handled := fieldSet{ - "metadata": true, - "type": true, - "credentials": true, - "config": true, - "credential_expiration_times": true, - "profile_workspace": true, - "credential_handles": true, - } - - assertAllFieldsCovered(t, (&dm.Provider{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_CredentialHandle(t *testing.T) { - handled := fieldSet{ - "driver": true, - "handle": true, - "metadata": true, - } - - assertAllFieldsCovered(t, (&dm.CredentialHandle{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_SandboxPolicyRevision(t *testing.T) { - handled := fieldSet{ - "version": true, - "policy_hash": true, - "status": true, - "load_error": true, - "created_time": true, - "loaded_time": true, - "policy": true, - "provenance": true, - } - - assertAllFieldsCovered(t, (&pb.SandboxPolicyRevision{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_ProviderProfile(t *testing.T) { - handled := fieldSet{ - "id": true, - "display_name": true, - "description": true, - "category": true, - "credentials": true, - "files": true, - "endpoints": true, - "binaries": true, - "inference_capable": true, - "discovery": true, - "resource_version": true, - "annotations": true, - "source": true, - "scope": true, - } - - assertAllFieldsCovered(t, (&pb.ProviderProfile{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_ProviderProfileCredential(t *testing.T) { - handled := fieldSet{ - "name": true, - "description": true, - "env_vars": true, - "required": true, - "auth_style": true, - "header_name": true, - "query_param": true, - "refresh": true, - "path_template": true, - "token_grant": true, - "stable_placeholder": true, - } - - // Resolved grant authorities belong to gateway-to-supervisor delivery, - // not authored provider profiles. They remain available in the raw API. - skipped := fieldSet{ - "token_grant_owners": true, - } - assertAllFieldsCovered(t, (&pb.ProviderProfileCredential{}).ProtoReflect().Descriptor(), handled, skipped) -} - -func TestConverterCoversAllProtoFields_ProviderCredentialTokenGrant(t *testing.T) { - handled := fieldSet{ - "token_endpoint": true, - "audience": true, - "jwt_svid_audience": true, - "scopes": true, - "cache_ttl": true, - "audience_overrides": true, - "client_assertion_type": true, - "grant_type": true, - "subject_token": true, - "requested_token_type": true, - } - - assertAllFieldsCovered(t, (&pb.ProviderCredentialTokenGrant{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_ProviderCredentialTokenGrantAudienceOverride(t *testing.T) { - handled := fieldSet{ - "host": true, - "port": true, - "path": true, - "audience": true, - "scopes": true, - } - - assertAllFieldsCovered(t, (&pb.ProviderCredentialTokenGrantAudienceOverride{}).ProtoReflect().Descriptor(), handled, nil) -} - -func TestConverterCoversAllProtoFields_McpOptions(t *testing.T) { - handled := fieldSet{ - "strict_tool_names": true, - "allow_all_known_mcp_methods": true, - "versions": true, - } - - assertAllFieldsCovered(t, (&sandboxpb.McpOptions{}).ProtoReflect().Descriptor(), handled, nil) -} - -// fieldSet tracks proto field names that the converter handles. -type fieldSet map[string]bool - -// assertAllFieldsCovered fails the test for proto fields not present in -// either handled or skipped. Stale entries in the handled set (fields -// removed from the proto) also fail. -func assertAllFieldsCovered( - t *testing.T, - desc protoreflect.MessageDescriptor, - handled fieldSet, - skipped fieldSet, -) { - t.Helper() - - fields := desc.Fields() - for i := 0; i < fields.Len(); i++ { - name := string(fields.Get(i).Name()) - if handled[name] || skipped[name] { - continue - } - t.Errorf( - "proto %s field %q is not handled by the converter and not explicitly skipped. "+ - "Add converter support in the appropriate FromProto/ToProto function, "+ - "or add it to the skipped set with a justification.", - desc.FullName(), name, - ) - } - - for name := range handled { - found := false - for i := 0; i < fields.Len(); i++ { - if string(fields.Get(i).Name()) == name { - found = true - break - } - } - if !found { - t.Errorf( - "handled field %q is listed for proto %s but does not exist in the descriptor. "+ - "The proto field may have been removed or renamed.", - name, desc.FullName(), - ) - } - } -} diff --git a/sdk/go/openshell/v1/internal/converter/errors.go b/sdk/go/openshell/v1/internal/converter/errors.go deleted file mode 100644 index f6457e27b3..0000000000 --- a/sdk/go/openshell/v1/internal/converter/errors.go +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package converter maps between gRPC/proto types and SDK domain types. -package converter - -import ( - "maps" - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - "google.golang.org/genproto/googleapis/rpc/errdetails" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -var grpcToSDK = map[codes.Code]types.ErrorCode{ - codes.NotFound: types.ErrorNotFound, - codes.AlreadyExists: types.ErrorAlreadyExists, - codes.Unavailable: types.ErrorUnavailable, - codes.PermissionDenied: types.ErrorPermissionDenied, - codes.InvalidArgument: types.ErrorInvalidArgument, - codes.DeadlineExceeded: types.ErrorDeadlineExceeded, - codes.Canceled: types.ErrorCancelled, - codes.Internal: types.ErrorInternal, - codes.Unimplemented: types.ErrorUnimplemented, - codes.Aborted: types.ErrorConflict, - codes.Unauthenticated: types.ErrorUnauthenticated, - codes.FailedPrecondition: types.ErrorConflict, - codes.ResourceExhausted: types.ErrorUnavailable, - codes.OutOfRange: types.ErrorInvalidArgument, -} - -// FromGRPCError converts a gRPC error to a typed StatusError. -// Returns nil for nil errors and OK status. Non-gRPC errors pass through unchanged. -func FromGRPCError(err error) error { - if err == nil { - return nil - } - - st, ok := status.FromError(err) - if !ok { - return err - } - - if st.Code() == codes.OK { - return nil - } - - code, mapped := grpcToSDK[st.Code()] - if !mapped { - code = types.ErrorInternal - } - - result := &types.StatusError{ - Code: code, - Message: st.Message(), - Cause: err, - GRPCCode: int32(st.Code()), - } - for _, detail := range st.Details() { - switch detail := detail.(type) { - case *errdetails.BadRequest: - for _, violation := range detail.GetFieldViolations() { - result.FieldViolations = append(result.FieldViolations, types.FieldViolation{ - Field: violation.GetField(), Description: violation.GetDescription(), - }) - } - case *errdetails.ErrorInfo: - result.ErrorInfo = &types.ErrorInfo{ - Reason: detail.GetReason(), Domain: detail.GetDomain(), - Metadata: maps.Clone(detail.GetMetadata()), - } - case *errdetails.RetryInfo: - delay := detail.GetRetryDelay() - if delay != nil && delay.CheckValid() == nil && delay.GetSeconds() >= 0 && delay.GetNanos() >= 0 { - // AsDuration saturates values that exceed time.Duration's range. - value := time.Duration(delay.AsDuration()) - result.RetryDelay = &value - } - } - } - return result -} diff --git a/sdk/go/openshell/v1/internal/converter/errors_test.go b/sdk/go/openshell/v1/internal/converter/errors_test.go deleted file mode 100644 index 6a948bfd33..0000000000 --- a/sdk/go/openshell/v1/internal/converter/errors_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - "time" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/genproto/googleapis/rpc/errdetails" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/wrapperspb" -) - -func TestFromGRPCError_PreservesStructuredAndUnknownDetails(t *testing.T) { - st, err := status.New(codes.ResourceExhausted, "try later").WithDetails( - &errdetails.BadRequest{FieldViolations: []*errdetails.BadRequest_FieldViolation{ - {Field: "name", Description: "too long"}, - }}, - &errdetails.ErrorInfo{Reason: "RATE_LIMITED", Domain: "openshell.nvidia.com", Metadata: map[string]string{"key": "value"}}, - &errdetails.RetryInfo{RetryDelay: durationpb.New(time.Second)}, - wrapperspb.String("unrecognized detail"), - ) - require.NoError(t, err) - raw := st.Err() - converted := FromGRPCError(raw) - var typed *v1.StatusError - require.ErrorAs(t, converted, &typed) - assert.Equal(t, int32(codes.ResourceExhausted), typed.GRPCCode) - assert.Equal(t, []v1.FieldViolation{{Field: "name", Description: "too long"}}, typed.FieldViolations) - require.NotNil(t, typed.ErrorInfo) - assert.Equal(t, "RATE_LIMITED", typed.ErrorInfo.Reason) - require.NotNil(t, typed.RetryDelay) - assert.Equal(t, time.Second, *typed.RetryDelay) - assert.Same(t, raw, typed.Cause) - // errors.Unwrap/status.FromError still exposes every original Any. - assert.Equal(t, st.Proto().GetDetails(), status.Convert(converted).Proto().GetDetails()) - typed.ErrorInfo.Metadata["key"] = "changed" - assert.Equal(t, "value", st.Details()[1].(*errdetails.ErrorInfo).GetMetadata()["key"]) -} - -func TestFromGRPCError_IgnoresInvalidRetryDelay(t *testing.T) { - st, err := status.New(codes.Unavailable, "try later").WithDetails( - &errdetails.RetryInfo{RetryDelay: durationpb.New(-time.Second)}, - ) - require.NoError(t, err) - var typed *v1.StatusError - require.ErrorAs(t, FromGRPCError(st.Err()), &typed) - assert.Nil(t, typed.RetryDelay) -} - -func TestFromGRPCError_NotFound(t *testing.T) { - grpcErr := status.Error(codes.NotFound, "sandbox not found") - err := FromGRPCError(grpcErr) - require.Error(t, err) - assert.True(t, v1.IsNotFound(err)) -} - -func TestFromGRPCError_AlreadyExists(t *testing.T) { - grpcErr := status.Error(codes.AlreadyExists, "already exists") - err := FromGRPCError(grpcErr) - require.Error(t, err) - assert.True(t, v1.IsAlreadyExists(err)) -} - -func TestFromGRPCError_Unavailable(t *testing.T) { - grpcErr := status.Error(codes.Unavailable, "service down") - err := FromGRPCError(grpcErr) - require.Error(t, err) - assert.True(t, v1.IsUnavailable(err)) -} - -func TestFromGRPCError_PermissionDenied(t *testing.T) { - grpcErr := status.Error(codes.PermissionDenied, "denied") - err := FromGRPCError(grpcErr) - require.Error(t, err) - assert.True(t, v1.IsPermissionDenied(err)) -} - -func TestFromGRPCError_InvalidArgument(t *testing.T) { - grpcErr := status.Error(codes.InvalidArgument, "bad arg") - err := FromGRPCError(grpcErr) - require.Error(t, err) - assert.True(t, v1.IsInvalidArgument(err)) -} - -func TestFromGRPCError_DeadlineExceeded(t *testing.T) { - grpcErr := status.Error(codes.DeadlineExceeded, "timeout") - err := FromGRPCError(grpcErr) - require.Error(t, err) - assert.True(t, v1.IsDeadlineExceeded(err)) -} - -func TestFromGRPCError_Cancelled(t *testing.T) { - grpcErr := status.Error(codes.Canceled, "cancelled") - err := FromGRPCError(grpcErr) - require.Error(t, err) - assert.True(t, v1.IsCancelled(err)) -} - -func TestFromGRPCError_Internal(t *testing.T) { - grpcErr := status.Error(codes.Internal, "internal error") - err := FromGRPCError(grpcErr) - require.Error(t, err) - - var se *v1.StatusError - require.ErrorAs(t, err, &se) - assert.Equal(t, v1.ErrorInternal, se.Code) -} - -func TestFromGRPCError_Unimplemented(t *testing.T) { - grpcErr := status.Error(codes.Unimplemented, "not implemented") - err := FromGRPCError(grpcErr) - require.Error(t, err) - - var se *v1.StatusError - require.ErrorAs(t, err, &se) - assert.Equal(t, v1.ErrorUnimplemented, se.Code) -} - -func TestFromGRPCError_Aborted(t *testing.T) { - grpcErr := status.Error(codes.Aborted, "version conflict") - err := FromGRPCError(grpcErr) - require.Error(t, err) - assert.True(t, v1.IsConflict(err)) - - var se *v1.StatusError - require.ErrorAs(t, err, &se) - assert.Equal(t, v1.ErrorConflict, se.Code) - assert.Equal(t, "version conflict", se.Message) -} - -func TestFromGRPCError_UnmappedCode(t *testing.T) { - grpcErr := status.Error(codes.DataLoss, "data loss") - err := FromGRPCError(grpcErr) - require.Error(t, err) - - var se *v1.StatusError - require.ErrorAs(t, err, &se) - assert.Equal(t, v1.ErrorInternal, se.Code) -} - -func TestFromGRPCError_NilError(t *testing.T) { - err := FromGRPCError(nil) - assert.NoError(t, err) -} - -func TestFromGRPCError_NonGRPCError(t *testing.T) { - err := FromGRPCError(assert.AnError) - require.Error(t, err) - assert.Equal(t, assert.AnError, err) -} - -func TestFromGRPCError_OKStatus(t *testing.T) { - grpcErr := status.Error(codes.OK, "") - err := FromGRPCError(grpcErr) - assert.NoError(t, err) -} diff --git a/sdk/go/openshell/v1/internal/converter/exec.go b/sdk/go/openshell/v1/internal/converter/exec.go deleted file mode 100644 index 9bb53ec011..0000000000 --- a/sdk/go/openshell/v1/internal/converter/exec.go +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "fmt" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" -) - -// ExecChunkFromEvent converts a proto ExecSandboxEvent to an ExecChunk and/or exit code. -// For stdout/stderr events, returns the chunk with exitCode -1. -// For exit events, returns nil chunk with the exit code. -func ExecChunkFromEvent(event *pb.ExecSandboxEvent) (*types.ExecChunk, int, error) { - if event == nil { - return nil, -1, fmt.Errorf("nil exec event") - } - - switch p := event.Payload.(type) { - case *pb.ExecSandboxEvent_Stdout: - return &types.ExecChunk{ - Stream: types.StreamStdout, - Data: append([]byte(nil), p.Stdout.GetData()...), - }, -1, nil - case *pb.ExecSandboxEvent_Stderr: - return &types.ExecChunk{ - Stream: types.StreamStderr, - Data: append([]byte(nil), p.Stderr.GetData()...), - }, -1, nil - case *pb.ExecSandboxEvent_Exit: - return nil, int(p.Exit.GetExitCode()), nil - default: - return nil, -1, fmt.Errorf("unknown exec event payload type: %T", p) - } -} - -// ExecRequestToProto builds a proto ExecSandboxRequest for Run/Stream modes. -func ExecRequestToProto(sandboxName string, command []string, opts *types.ExecOptions) *pb.ExecSandboxRequest { - req := &pb.ExecSandboxRequest{ - Sandbox: sandboxName, - Command: CopyStringSlice(command), - } - if opts != nil { - req.Workdir = opts.WorkDir - req.Environment = CopyStringMap(opts.Env) - req.NoLoginShell = opts.NoLoginShell - } - return req -} - -// ExecInteractiveRequestToProto builds a proto ExecSandboxRequest for Interactive mode. -func ExecInteractiveRequestToProto(sandboxName string, command []string, cols, rows uint32, opts *types.ExecOptions) *pb.ExecSandboxRequest { - req := ExecRequestToProto(sandboxName, command, opts) - req.Tty = true - req.Cols = cols - req.Rows = rows - return req -} - -// ExecResultFromEvents collects a sequence of ExecSandboxEvents into an ExecResult. -func ExecResultFromEvents(events []*pb.ExecSandboxEvent) (*types.ExecResult, error) { - if len(events) == 0 { - return nil, fmt.Errorf("no exec events received") - } - - var stdout, stderr []byte - exitCode := -1 - sawExit := false - - for _, event := range events { - chunk, code, err := ExecChunkFromEvent(event) - if err != nil { - return nil, err - } - if chunk != nil { - switch chunk.Stream { - case types.StreamStdout: - stdout = append(stdout, chunk.Data...) - case types.StreamStderr: - stderr = append(stderr, chunk.Data...) - } - } else { - exitCode = code - sawExit = true - } - } - - if !sawExit { - return nil, fmt.Errorf("no exit event received") - } - - return &types.ExecResult{ - ExitCode: exitCode, - Stdout: stdout, - Stderr: stderr, - }, nil -} diff --git a/sdk/go/openshell/v1/internal/converter/exec_test.go b/sdk/go/openshell/v1/internal/converter/exec_test.go deleted file mode 100644 index daac515c64..0000000000 --- a/sdk/go/openshell/v1/internal/converter/exec_test.go +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestExecChunkFromEvent_Stdout(t *testing.T) { - event := &pb.ExecSandboxEvent{ - Payload: &pb.ExecSandboxEvent_Stdout{ - Stdout: &pb.ExecSandboxStdout{ - Data: []byte("hello world"), - }, - }, - } - - chunk, exitCode, err := ExecChunkFromEvent(event) - - require.NoError(t, err) - require.NotNil(t, chunk) - assert.Equal(t, v1.StreamStdout, chunk.Stream) - assert.Equal(t, []byte("hello world"), chunk.Data) - assert.Equal(t, -1, exitCode) -} - -func TestExecChunkFromEvent_Stderr(t *testing.T) { - event := &pb.ExecSandboxEvent{ - Payload: &pb.ExecSandboxEvent_Stderr{ - Stderr: &pb.ExecSandboxStderr{ - Data: []byte("error output"), - }, - }, - } - - chunk, exitCode, err := ExecChunkFromEvent(event) - - require.NoError(t, err) - require.NotNil(t, chunk) - assert.Equal(t, v1.StreamStderr, chunk.Stream) - assert.Equal(t, []byte("error output"), chunk.Data) - assert.Equal(t, -1, exitCode) -} - -func TestExecChunkFromEvent_Exit(t *testing.T) { - event := &pb.ExecSandboxEvent{ - Payload: &pb.ExecSandboxEvent_Exit{ - Exit: &pb.ExecSandboxExit{ - ExitCode: 42, - }, - }, - } - - chunk, exitCode, err := ExecChunkFromEvent(event) - - require.NoError(t, err) - assert.Nil(t, chunk) - assert.Equal(t, 42, exitCode) -} - -func TestExecChunkFromEvent_ExitZero(t *testing.T) { - event := &pb.ExecSandboxEvent{ - Payload: &pb.ExecSandboxEvent_Exit{ - Exit: &pb.ExecSandboxExit{ - ExitCode: 0, - }, - }, - } - - chunk, exitCode, err := ExecChunkFromEvent(event) - - require.NoError(t, err) - assert.Nil(t, chunk) - assert.Equal(t, 0, exitCode) -} - -func TestExecChunkFromEvent_NilEvent(t *testing.T) { - _, _, err := ExecChunkFromEvent(nil) - assert.Error(t, err) -} - -func TestExecChunkFromEvent_NilPayload(t *testing.T) { - event := &pb.ExecSandboxEvent{} - - _, _, err := ExecChunkFromEvent(event) - assert.Error(t, err) -} - -func TestExecChunkFromEvent_EmptyStdout(t *testing.T) { - event := &pb.ExecSandboxEvent{ - Payload: &pb.ExecSandboxEvent_Stdout{ - Stdout: &pb.ExecSandboxStdout{ - Data: []byte{}, - }, - }, - } - - chunk, exitCode, err := ExecChunkFromEvent(event) - - require.NoError(t, err) - require.NotNil(t, chunk) - assert.Equal(t, v1.StreamStdout, chunk.Stream) - assert.Empty(t, chunk.Data) - assert.Equal(t, -1, exitCode) -} - -func TestExecRequestToProto(t *testing.T) { - req := ExecRequestToProto("sb-1", []string{"ls", "-la"}, &v1.ExecOptions{ - Env: map[string]string{"FOO": "bar"}, - WorkDir: "/home/user", - }) - - require.NotNil(t, req) - assert.Equal(t, "sb-1", req.GetSandbox()) - assert.Equal(t, []string{"ls", "-la"}, req.Command) - assert.Equal(t, "/home/user", req.Workdir) - assert.Equal(t, map[string]string{"FOO": "bar"}, req.Environment) - assert.False(t, req.Tty) -} - -func TestExecRequestToProto_NilOptions(t *testing.T) { - req := ExecRequestToProto("sb-2", []string{"echo", "hi"}, nil) - - require.NotNil(t, req) - assert.Equal(t, "sb-2", req.GetSandbox()) - assert.Equal(t, []string{"echo", "hi"}, req.Command) - assert.Empty(t, req.Workdir) - assert.Nil(t, req.Environment) -} - -func TestExecRequestToProto_Interactive(t *testing.T) { - req := ExecInteractiveRequestToProto("sb-3", []string{"/bin/bash"}, 80, 24, &v1.ExecOptions{ - Env: map[string]string{"TERM": "xterm"}, - WorkDir: "/root", - }) - - require.NotNil(t, req) - assert.Equal(t, "sb-3", req.GetSandbox()) - assert.Equal(t, []string{"/bin/bash"}, req.Command) - assert.Equal(t, "/root", req.Workdir) - assert.Equal(t, map[string]string{"TERM": "xterm"}, req.Environment) - assert.True(t, req.Tty) - assert.Equal(t, uint32(80), req.Cols) - assert.Equal(t, uint32(24), req.Rows) -} - -func TestExecResultFromEvents(t *testing.T) { - events := []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("line1\n")}}}, - {Payload: &pb.ExecSandboxEvent_Stderr{Stderr: &pb.ExecSandboxStderr{Data: []byte("warn\n")}}}, - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("line2\n")}}}, - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 0}}}, - } - - result, err := ExecResultFromEvents(events) - - require.NoError(t, err) - assert.Equal(t, 0, result.ExitCode) - assert.Equal(t, []byte("line1\nline2\n"), result.Stdout) - assert.Equal(t, []byte("warn\n"), result.Stderr) -} - -func TestExecResultFromEvents_NoExit(t *testing.T) { - events := []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Stdout{Stdout: &pb.ExecSandboxStdout{Data: []byte("data")}}}, - } - - _, err := ExecResultFromEvents(events) - assert.Error(t, err) -} - -func TestExecResultFromEvents_Empty(t *testing.T) { - _, err := ExecResultFromEvents(nil) - assert.Error(t, err) -} - -func TestExecResultFromEvents_OnlyExit(t *testing.T) { - events := []*pb.ExecSandboxEvent{ - {Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{ExitCode: 1}}}, - } - - result, err := ExecResultFromEvents(events) - - require.NoError(t, err) - assert.Equal(t, 1, result.ExitCode) - assert.Empty(t, result.Stdout) - assert.Empty(t, result.Stderr) -} diff --git a/sdk/go/openshell/v1/internal/converter/health.go b/sdk/go/openshell/v1/internal/converter/health.go deleted file mode 100644 index 8e1c2b98f6..0000000000 --- a/sdk/go/openshell/v1/internal/converter/health.go +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" -) - -// GatewayInfoFromProto converts a proto GetGatewayInfoResponse to an SDK GatewayInfo. -func GatewayInfoFromProto(resp *pb.GetGatewayInfoResponse) *types.GatewayInfo { - if resp == nil { - return nil - } - - drivers := make([]types.ComputeDriverInfo, 0, len(resp.GetComputeDrivers())) - for _, d := range resp.GetComputeDrivers() { - drivers = append(drivers, ComputeDriverInfoFromProto(d)) - } - extensions := make([]types.ExtensionInfo, 0, len(resp.GetExtensions())) - for _, extension := range resp.GetExtensions() { - extensions = append(extensions, ExtensionInfoFromProto(extension)) - } - - return &types.GatewayInfo{ - Status: ServiceStatusFromProto(resp.GetStatus()), - Version: resp.GetGatewayVersion(), - ComputeDrivers: drivers, - Extensions: extensions, - } -} - -// ExtensionInfoFromProto converts a negotiated extension snapshot. -func ExtensionInfoFromProto(extension *pb.NegotiatedExtensionInfo) types.ExtensionInfo { - return types.ExtensionInfo{ - Kind: ExtensionKindFromProto(extension.GetKind()), - ConfiguredName: extension.GetConfiguredName(), - ImplementationName: extension.GetImplementationName(), - ImplementationVersion: extension.GetImplementationVersion(), - ProtocolMajor: extension.GetProtocolMajor(), - ProtocolMinor: extension.GetProtocolMinor(), - SupportedCapabilities: CopyStringSlice(extension.GetSupportedCapabilities()), - RequiredCapabilities: CopyStringSlice(extension.GetRequiredCapabilities()), - } -} - -// ExtensionKindFromProto converts the public extension family enum. -func ExtensionKindFromProto(kind pb.ExtensionKind) types.ExtensionKind { - switch kind { - case pb.ExtensionKind_EXTENSION_KIND_COMPUTE_DRIVER: - return types.ExtensionKindComputeDriver - case pb.ExtensionKind_EXTENSION_KIND_CREDENTIAL_DRIVER: - return types.ExtensionKindCredentialDriver - case pb.ExtensionKind_EXTENSION_KIND_GATEWAY_INTERCEPTOR: - return types.ExtensionKindGatewayInterceptor - case pb.ExtensionKind_EXTENSION_KIND_SUPERVISOR_MIDDLEWARE: - return types.ExtensionKindSupervisorMiddleware - default: - return types.ExtensionKindUnknown - } -} - -// ServiceStatusFromProto converts a proto ServiceStatus to an SDK ServiceStatus. -func ServiceStatusFromProto(status pb.ServiceStatus) types.ServiceStatus { - switch status { - case pb.ServiceStatus_SERVICE_STATUS_HEALTHY: - return types.ServiceStatusHealthy - case pb.ServiceStatus_SERVICE_STATUS_DEGRADED: - return types.ServiceStatusDegraded - case pb.ServiceStatus_SERVICE_STATUS_UNHEALTHY: - return types.ServiceStatusUnhealthy - default: - return types.ServiceStatusUnknown - } -} - -// ComputeDriverInfoFromProto converts a proto ComputeDriverInfo to an SDK ComputeDriverInfo. -func ComputeDriverInfoFromProto(d *pb.ComputeDriverInfo) types.ComputeDriverInfo { - result := types.ComputeDriverInfo{ - Name: d.GetName(), - } - if caps := d.GetCapabilities(); caps != nil { - result.DriverName = caps.GetDriverName() - result.DriverVersion = caps.GetDriverVersion() - } - return result -} - -// CurrentUserFromProto converts a proto GetCurrentUserResponse to an SDK CurrentUser. -func CurrentUserFromProto(resp *pb.GetCurrentUserResponse) *types.CurrentUser { - if resp == nil { - return nil - } - - return &types.CurrentUser{ - Subject: resp.GetSubject(), - DisplayName: resp.GetDisplayName(), - Roles: CopyStringSlice(resp.GetRoles()), - Scopes: CopyStringSlice(resp.GetScopes()), - IdentityProvider: resp.GetIdentityProvider(), - } -} diff --git a/sdk/go/openshell/v1/internal/converter/health_test.go b/sdk/go/openshell/v1/internal/converter/health_test.go deleted file mode 100644 index ab8e0c2415..0000000000 --- a/sdk/go/openshell/v1/internal/converter/health_test.go +++ /dev/null @@ -1,185 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGatewayInfoFromProto(t *testing.T) { - proto := &pb.GetGatewayInfoResponse{ - Status: pb.ServiceStatus_SERVICE_STATUS_HEALTHY, - GatewayVersion: "1.5.0", - ComputeDrivers: []*pb.ComputeDriverInfo{ - { - Name: "k8s", - Capabilities: &pb.ComputeDriverCapabilities{ - DriverName: "kubernetes", - DriverVersion: "2.1.0", - }, - }, - { - Name: "docker", - Capabilities: &pb.ComputeDriverCapabilities{ - DriverName: "docker-engine", - DriverVersion: "24.0.0", - }, - }, - }, - Extensions: []*pb.NegotiatedExtensionInfo{ - { - Kind: pb.ExtensionKind_EXTENSION_KIND_COMPUTE_DRIVER, - ConfiguredName: "k8s", - ImplementationName: "openshell/kubernetes", - ImplementationVersion: "1.5.0", - ProtocolMajor: 1, - SupportedCapabilities: []string{"openshell.compute.contract"}, - RequiredCapabilities: []string{"openshell.compute.contract"}, - }, - }, - } - - info := GatewayInfoFromProto(proto) - - require.NotNil(t, info) - assert.Equal(t, v1.ServiceStatusHealthy, info.Status) - assert.Equal(t, "1.5.0", info.Version) - require.Len(t, info.ComputeDrivers, 2) - assert.Equal(t, "k8s", info.ComputeDrivers[0].Name) - assert.Equal(t, "kubernetes", info.ComputeDrivers[0].DriverName) - assert.Equal(t, "2.1.0", info.ComputeDrivers[0].DriverVersion) - assert.Equal(t, "docker", info.ComputeDrivers[1].Name) - assert.Equal(t, "docker-engine", info.ComputeDrivers[1].DriverName) - require.Len(t, info.Extensions, 1) - assert.Equal(t, v1.ExtensionKindComputeDriver, info.Extensions[0].Kind) - assert.Equal(t, "openshell/kubernetes", info.Extensions[0].ImplementationName) - assert.Equal(t, uint32(1), info.Extensions[0].ProtocolMajor) -} - -func TestGatewayInfoFromProto_NoDrivers(t *testing.T) { - proto := &pb.GetGatewayInfoResponse{ - Status: pb.ServiceStatus_SERVICE_STATUS_DEGRADED, - GatewayVersion: "1.0.0", - } - - info := GatewayInfoFromProto(proto) - - require.NotNil(t, info) - assert.Equal(t, v1.ServiceStatusDegraded, info.Status) - assert.Empty(t, info.ComputeDrivers) -} - -func TestGatewayInfoFromProto_Nil(t *testing.T) { - info := GatewayInfoFromProto(nil) - assert.Nil(t, info) -} - -func TestGatewayInfoFromProto_DeepCopy(t *testing.T) { - proto := &pb.GetGatewayInfoResponse{ - Status: pb.ServiceStatus_SERVICE_STATUS_HEALTHY, - GatewayVersion: "1.0.0", - ComputeDrivers: []*pb.ComputeDriverInfo{ - {Name: "k8s", Capabilities: &pb.ComputeDriverCapabilities{DriverName: "kubernetes"}}, - }, - Extensions: []*pb.NegotiatedExtensionInfo{{ - ConfiguredName: "k8s", - SupportedCapabilities: []string{"openshell.compute.contract"}, - }}, - } - - info := GatewayInfoFromProto(proto) - proto.ComputeDrivers[0].Name = "mutated" - proto.Extensions[0].ConfiguredName = "mutated" - proto.Extensions[0].SupportedCapabilities[0] = "mutated" - - assert.Equal(t, "k8s", info.ComputeDrivers[0].Name) - assert.Equal(t, "k8s", info.Extensions[0].ConfiguredName) - assert.Equal(t, "openshell.compute.contract", info.Extensions[0].SupportedCapabilities[0]) -} - -func TestServiceStatusFromProto(t *testing.T) { - tests := []struct { - proto pb.ServiceStatus - expected v1.ServiceStatus - }{ - {pb.ServiceStatus_SERVICE_STATUS_HEALTHY, v1.ServiceStatusHealthy}, - {pb.ServiceStatus_SERVICE_STATUS_DEGRADED, v1.ServiceStatusDegraded}, - {pb.ServiceStatus_SERVICE_STATUS_UNHEALTHY, v1.ServiceStatusUnhealthy}, - {pb.ServiceStatus_SERVICE_STATUS_UNSPECIFIED, v1.ServiceStatusUnknown}, - {pb.ServiceStatus(99), v1.ServiceStatusUnknown}, - } - - for _, tt := range tests { - assert.Equal(t, tt.expected, ServiceStatusFromProto(tt.proto)) - } -} - -func TestComputeDriverInfoFromProto_NilCapabilities(t *testing.T) { - proto := &pb.ComputeDriverInfo{ - Name: "bare-metal", - } - - info := ComputeDriverInfoFromProto(proto) - - assert.Equal(t, "bare-metal", info.Name) - assert.Empty(t, info.DriverName) - assert.Empty(t, info.DriverVersion) -} - -func TestCurrentUserFromProto(t *testing.T) { - proto := &pb.GetCurrentUserResponse{ - Subject: "user-123", - DisplayName: "Test User", - Roles: []string{"admin", "viewer"}, - Scopes: []string{"read", "write"}, - IdentityProvider: "oidc-provider", - } - - user := CurrentUserFromProto(proto) - - require.NotNil(t, user) - assert.Equal(t, "user-123", user.Subject) - assert.Equal(t, "Test User", user.DisplayName) - assert.Equal(t, []string{"admin", "viewer"}, user.Roles) - assert.Equal(t, []string{"read", "write"}, user.Scopes) - assert.Equal(t, "oidc-provider", user.IdentityProvider) -} - -func TestCurrentUserFromProto_DeepCopy(t *testing.T) { - roles := []string{"admin"} - proto := &pb.GetCurrentUserResponse{ - Subject: "user-1", - Roles: roles, - } - - user := CurrentUserFromProto(proto) - roles[0] = "mutated" - - assert.Equal(t, "admin", user.Roles[0]) -} - -func TestCurrentUserFromProto_Nil(t *testing.T) { - user := CurrentUserFromProto(nil) - assert.Nil(t, user) -} - -func TestCurrentUserFromProto_EmptyFields(t *testing.T) { - proto := &pb.GetCurrentUserResponse{ - Subject: "minimal-user", - } - - user := CurrentUserFromProto(proto) - - require.NotNil(t, user) - assert.Equal(t, "minimal-user", user.Subject) - assert.Empty(t, user.DisplayName) - assert.Nil(t, user.Roles) - assert.Nil(t, user.Scopes) - assert.Empty(t, user.IdentityProvider) -} diff --git a/sdk/go/openshell/v1/internal/converter/log.go b/sdk/go/openshell/v1/internal/converter/log.go deleted file mode 100644 index 9a10e51987..0000000000 --- a/sdk/go/openshell/v1/internal/converter/log.go +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" -) - -// --- LogLine --- - -// LogLineFromProto converts a proto SandboxLogLine to an SDK LogLine. -func LogLineFromProto(l *pb.SandboxLogLine) *types.LogLine { - if l == nil { - return nil - } - return &types.LogLine{ - Timestamp: TimeFromProto(l.GetEventTime()), - Level: l.GetLevel(), - Target: l.GetTarget(), - Message: l.GetMessage(), - Source: l.GetSource(), - Fields: CopyStringMap(l.GetFields()), - } -} - -// --- LogResult --- - -// LogResultFromProto converts a proto GetSandboxLogsResponse to an SDK LogResult. -func LogResultFromProto(r *pb.GetSandboxLogsResponse) *types.LogResult { - if r == nil { - return nil - } - result := &types.LogResult{ - BufferTotal: r.GetBufferTotal(), - } - if logs := r.GetLogs(); len(logs) > 0 { - result.Lines = make([]types.LogLine, 0, len(logs)) - for _, l := range logs { - if converted := LogLineFromProto(l); converted != nil { - result.Lines = append(result.Lines, *converted) - } - } - } - return result -} diff --git a/sdk/go/openshell/v1/internal/converter/log_test.go b/sdk/go/openshell/v1/internal/converter/log_test.go deleted file mode 100644 index 6bd285b763..0000000000 --- a/sdk/go/openshell/v1/internal/converter/log_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// --- LogLine --- - -func TestLogLineFromProto(t *testing.T) { - proto := &pb.SandboxLogLine{ - SandboxId: "sbx-1", - EventTime: TimestampFromMillis(1700000000000), - Level: "INFO", - Target: "network", - Message: "Connection established", - Source: "sandbox-agent", - Fields: map[string]string{ - "host": "api.example.com", - "port": "443", - }, - } - - line := LogLineFromProto(proto) - - require.NotNil(t, line) - assert.False(t, line.Timestamp.IsZero()) - assert.Equal(t, "INFO", line.Level) - assert.Equal(t, "network", line.Target) - assert.Equal(t, "Connection established", line.Message) - assert.Equal(t, "sandbox-agent", line.Source) - assert.Equal(t, "api.example.com", line.Fields["host"]) - assert.Equal(t, "443", line.Fields["port"]) -} - -func TestLogLineFromProto_Nil(t *testing.T) { - assert.Nil(t, LogLineFromProto(nil)) -} - -func TestLogLineDeepCopy(t *testing.T) { - proto := &pb.SandboxLogLine{ - EventTime: TimestampFromMillis(1700000000000), - Level: "WARN", - Message: "test", - Fields: map[string]string{ - "key": "value", - }, - } - - line := LogLineFromProto(proto) - proto.Fields["key"] = "changed" - - assert.Equal(t, "value", line.Fields["key"]) -} - -// --- LogResult --- - -func TestLogResultFromProto(t *testing.T) { - proto := &pb.GetSandboxLogsResponse{ - Logs: []*pb.SandboxLogLine{ - {EventTime: TimestampFromMillis(1700000000000), Level: "INFO", Message: "first"}, - {EventTime: TimestampFromMillis(1700000001000), Level: "DEBUG", Message: "second"}, - }, - BufferTotal: 100, - } - - result := LogResultFromProto(proto) - - require.NotNil(t, result) - assert.Len(t, result.Lines, 2) - assert.Equal(t, "INFO", result.Lines[0].Level) - assert.Equal(t, "first", result.Lines[0].Message) - assert.Equal(t, "DEBUG", result.Lines[1].Level) - assert.Equal(t, "second", result.Lines[1].Message) - assert.Equal(t, uint32(100), result.BufferTotal) -} - -func TestLogResultFromProto_Nil(t *testing.T) { - assert.Nil(t, LogResultFromProto(nil)) -} - -func TestLogResultFromProto_EmptyLogs(t *testing.T) { - proto := &pb.GetSandboxLogsResponse{ - BufferTotal: 0, - } - - result := LogResultFromProto(proto) - require.NotNil(t, result) - assert.Empty(t, result.Lines) - assert.Equal(t, uint32(0), result.BufferTotal) -} diff --git a/sdk/go/openshell/v1/internal/converter/network_policy.go b/sdk/go/openshell/v1/internal/converter/network_policy.go deleted file mode 100644 index d51323ed67..0000000000 --- a/sdk/go/openshell/v1/internal/converter/network_policy.go +++ /dev/null @@ -1,335 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - sbv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" -) - -// --- NetworkPolicyRule --- - -// NetworkPolicyRuleFromProto converts a proto NetworkPolicyRule to an SDK NetworkPolicyRule. -func NetworkPolicyRuleFromProto(r *sbv1.NetworkPolicyRule) *types.NetworkPolicyRule { - if r == nil { - return nil - } - result := &types.NetworkPolicyRule{ - Name: r.GetName(), - } - if eps := r.GetEndpoints(); len(eps) > 0 { - result.Endpoints = make([]types.PolicyNetworkEndpoint, len(eps)) - for i, ep := range eps { - if ep != nil { - result.Endpoints[i] = policyNetworkEndpointFromProto(ep) - } - } - } - if bins := r.GetBinaries(); len(bins) > 0 { - result.Binaries = make([]types.PolicyNetworkBinary, len(bins)) - for i, b := range bins { - if b != nil { - result.Binaries[i] = types.PolicyNetworkBinary{Path: b.GetPath()} - } - } - } - return result -} - -// NetworkPolicyRuleToProto converts an SDK NetworkPolicyRule to a proto NetworkPolicyRule. -func NetworkPolicyRuleToProto(r *types.NetworkPolicyRule) *sbv1.NetworkPolicyRule { - if r == nil { - return nil - } - result := &sbv1.NetworkPolicyRule{ - Name: r.Name, - } - if len(r.Endpoints) > 0 { - result.Endpoints = make([]*sbv1.NetworkEndpoint, len(r.Endpoints)) - for i := range r.Endpoints { - result.Endpoints[i] = policyNetworkEndpointToProto(&r.Endpoints[i]) - } - } - if len(r.Binaries) > 0 { - result.Binaries = make([]*sbv1.NetworkBinary, len(r.Binaries)) - for i := range r.Binaries { - result.Binaries[i] = &sbv1.NetworkBinary{Path: r.Binaries[i].Path} - } - } - return result -} - -// --- PolicyNetworkEndpoint --- - -func policyNetworkEndpointFromProto(ep *sbv1.NetworkEndpoint) types.PolicyNetworkEndpoint { - result := types.PolicyNetworkEndpoint{ - Host: ep.GetHost(), - Port: ep.GetPort(), - Protocol: ep.GetProtocol(), - TLS: types.NetworkTLSMode(ep.GetTls()), - Enforcement: types.NetworkEnforcementMode(ep.GetEnforcement()), - Access: types.NetworkAccessPreset(ep.GetAccess()), - AllowEncodedSlash: ep.GetAllowEncodedSlash(), - PersistedQueries: ep.GetPersistedQueries(), - GraphqlMaxBodyBytes: ep.GetGraphqlMaxBodyBytes(), - Path: ep.GetPath(), - WebsocketCredentialRewrite: ep.GetWebsocketCredentialRewrite(), - RequestBodyCredentialRewrite: ep.GetRequestBodyCredentialRewrite(), - AllowUninspectedCredentials: ep.GetAllowUninspectedCredentials(), - ProviderCredentialed: ep.GetProviderCredentialed(), - AdvisorProposed: ep.GetAdvisorProposed(), - CredentialSigning: ep.GetCredentialSigning(), - SigningService: ep.GetSigningService(), - SigningRegion: ep.GetSigningRegion(), - JSONRPCMaxBodyBytes: ep.GetJsonRpcMaxBodyBytes(), - } - if binding := ep.GetCredentialBinding(); binding != nil { - result.CredentialBinding = &types.NetworkCredentialBinding{ - Provider: binding.GetProvider(), - } - } - if ports := ep.GetPorts(); len(ports) > 0 { - result.Ports = make([]uint32, len(ports)) - copy(result.Ports, ports) - } - if ips := ep.GetAllowedIps(); len(ips) > 0 { - result.AllowedIPs = CopyStringSlice(ips) - } - if rules := ep.GetRules(); len(rules) > 0 { - result.Rules = make([]types.L7Rule, len(rules)) - for i, r := range rules { - if r != nil { - result.Rules[i] = l7RuleFromProto(r) - } - } - } - if deny := ep.GetDenyRules(); len(deny) > 0 { - result.DenyRules = make([]types.L7DenyRule, len(deny)) - for i, r := range deny { - if r != nil { - result.DenyRules[i] = l7DenyRuleFromProto(r) - } - } - } - if gql := ep.GetGraphqlPersistedQueries(); len(gql) > 0 { - result.GraphqlPersistedQueries = make(map[string]types.GraphqlOperation, len(gql)) - for k, v := range gql { - if v != nil { - result.GraphqlPersistedQueries[k] = graphqlOperationFromProto(v) - } - } - } - result.Mcp = mcpOptionsFromProto(ep.GetMcp()) - return result -} - -func policyNetworkEndpointToProto(ep *types.PolicyNetworkEndpoint) *sbv1.NetworkEndpoint { - result := &sbv1.NetworkEndpoint{ - Host: ep.Host, - Port: ep.Port, - Protocol: ep.Protocol, - Tls: sbv1.NetworkTlsMode(ep.TLS), - Enforcement: sbv1.NetworkEnforcementMode(ep.Enforcement), - Access: sbv1.NetworkAccessPreset(ep.Access), - AllowEncodedSlash: ep.AllowEncodedSlash, - PersistedQueries: ep.PersistedQueries, - GraphqlMaxBodyBytes: ep.GraphqlMaxBodyBytes, - Path: ep.Path, - WebsocketCredentialRewrite: ep.WebsocketCredentialRewrite, - RequestBodyCredentialRewrite: ep.RequestBodyCredentialRewrite, - AllowUninspectedCredentials: ep.AllowUninspectedCredentials, - ProviderCredentialed: ep.ProviderCredentialed, - AdvisorProposed: ep.AdvisorProposed, - CredentialSigning: ep.CredentialSigning, - SigningService: ep.SigningService, - SigningRegion: ep.SigningRegion, - JsonRpcMaxBodyBytes: ep.JSONRPCMaxBodyBytes, - } - if ep.CredentialBinding != nil { - result.CredentialBinding = &sbv1.NetworkCredentialBinding{ - Provider: ep.CredentialBinding.Provider, - } - } - if len(ep.Ports) > 0 { - result.Ports = make([]uint32, len(ep.Ports)) - copy(result.Ports, ep.Ports) - } - if len(ep.AllowedIPs) > 0 { - result.AllowedIps = CopyStringSlice(ep.AllowedIPs) - } - if len(ep.Rules) > 0 { - result.Rules = make([]*sbv1.L7Rule, len(ep.Rules)) - for i := range ep.Rules { - result.Rules[i] = l7RuleToProto(&ep.Rules[i]) - } - } - if len(ep.DenyRules) > 0 { - result.DenyRules = make([]*sbv1.L7DenyRule, len(ep.DenyRules)) - for i := range ep.DenyRules { - result.DenyRules[i] = l7DenyRuleToProto(&ep.DenyRules[i]) - } - } - if len(ep.GraphqlPersistedQueries) > 0 { - result.GraphqlPersistedQueries = make(map[string]*sbv1.GraphqlOperation, len(ep.GraphqlPersistedQueries)) - for k, v := range ep.GraphqlPersistedQueries { - result.GraphqlPersistedQueries[k] = graphqlOperationToProto(&v) - } - } - result.Mcp = mcpOptionsToProto(ep.Mcp) - return result -} - -// --- McpOptions --- - -// mcpOptionsFromProto performs a transport conversion only. It leaves an empty -// version list empty because checked policy and server ingress own default -// materialization and validation. -func mcpOptionsFromProto(m *sbv1.McpOptions) *types.McpOptions { - if m == nil { - return nil - } - return &types.McpOptions{ - StrictToolNames: CopyBoolPtr(m.StrictToolNames), - AllowAllKnownMcpMethods: CopyBoolPtr(m.AllowAllKnownMcpMethods), - Versions: CopyStringSlice(m.GetVersions()), - } -} - -func mcpOptionsToProto(m *types.McpOptions) *sbv1.McpOptions { - if m == nil { - return nil - } - return &sbv1.McpOptions{ - StrictToolNames: CopyBoolPtr(m.StrictToolNames), - AllowAllKnownMcpMethods: CopyBoolPtr(m.AllowAllKnownMcpMethods), - Versions: CopyStringSlice(m.Versions), - } -} - -// --- L7Rule --- - -func l7RuleFromProto(r *sbv1.L7Rule) types.L7Rule { - result := types.L7Rule{} - if a := r.GetAllow(); a != nil { - result.Allow = &types.L7Allow{ - Method: a.GetMethod(), - Path: a.GetPath(), - Command: a.GetCommand(), - OperationType: a.GetOperationType(), - OperationName: a.GetOperationName(), - Fields: CopyStringSlice(a.GetFields()), - } - if q := a.GetQuery(); len(q) > 0 { - result.Allow.Query = l7QueryMapFromProto(q) - } - if p := a.GetParams(); len(p) > 0 { - result.Allow.Params = l7QueryMapFromProto(p) - } - } - return result -} - -func l7RuleToProto(r *types.L7Rule) *sbv1.L7Rule { - result := &sbv1.L7Rule{} - if r.Allow != nil { - result.Allow = &sbv1.L7Allow{ - Method: r.Allow.Method, - Path: r.Allow.Path, - Command: r.Allow.Command, - OperationType: r.Allow.OperationType, - OperationName: r.Allow.OperationName, - Fields: CopyStringSlice(r.Allow.Fields), - } - if len(r.Allow.Query) > 0 { - result.Allow.Query = l7QueryMapToProto(r.Allow.Query) - } - if len(r.Allow.Params) > 0 { - result.Allow.Params = l7QueryMapToProto(r.Allow.Params) - } - } - return result -} - -// --- L7DenyRule --- - -func l7DenyRuleFromProto(r *sbv1.L7DenyRule) types.L7DenyRule { - return types.L7DenyRule{ - Method: r.GetMethod(), - Path: r.GetPath(), - Command: r.GetCommand(), - OperationType: r.GetOperationType(), - OperationName: r.GetOperationName(), - Fields: CopyStringSlice(r.GetFields()), - Query: l7QueryMapFromProto(r.GetQuery()), - Params: l7QueryMapFromProto(r.GetParams()), - } -} - -func l7DenyRuleToProto(r *types.L7DenyRule) *sbv1.L7DenyRule { - result := &sbv1.L7DenyRule{ - Method: r.Method, - Path: r.Path, - Command: r.Command, - OperationType: r.OperationType, - OperationName: r.OperationName, - Fields: CopyStringSlice(r.Fields), - } - if len(r.Query) > 0 { - result.Query = l7QueryMapToProto(r.Query) - } - if len(r.Params) > 0 { - result.Params = l7QueryMapToProto(r.Params) - } - return result -} - -// --- L7QueryMatcher helpers --- - -func l7QueryMapFromProto(m map[string]*sbv1.L7QueryMatcher) map[string]types.L7QueryMatcher { - if len(m) == 0 { - return nil - } - result := make(map[string]types.L7QueryMatcher, len(m)) - for k, v := range m { - if v != nil { - result[k] = types.L7QueryMatcher{ - Glob: v.GetGlob(), - Any: CopyStringSlice(v.GetAny()), - } - } - } - return result -} - -func l7QueryMapToProto(m map[string]types.L7QueryMatcher) map[string]*sbv1.L7QueryMatcher { - if len(m) == 0 { - return nil - } - result := make(map[string]*sbv1.L7QueryMatcher, len(m)) - for k, v := range m { - result[k] = &sbv1.L7QueryMatcher{ - Glob: v.Glob, - Any: CopyStringSlice(v.Any), - } - } - return result -} - -// --- GraphqlOperation --- - -func graphqlOperationFromProto(op *sbv1.GraphqlOperation) types.GraphqlOperation { - return types.GraphqlOperation{ - OperationType: op.GetOperationType(), - OperationName: op.GetOperationName(), - Fields: CopyStringSlice(op.GetFields()), - } -} - -func graphqlOperationToProto(op *types.GraphqlOperation) *sbv1.GraphqlOperation { - return &sbv1.GraphqlOperation{ - OperationType: op.OperationType, - OperationName: op.OperationName, - Fields: CopyStringSlice(op.Fields), - } -} diff --git a/sdk/go/openshell/v1/internal/converter/network_policy_test.go b/sdk/go/openshell/v1/internal/converter/network_policy_test.go deleted file mode 100644 index 6a4d73dc6f..0000000000 --- a/sdk/go/openshell/v1/internal/converter/network_policy_test.go +++ /dev/null @@ -1,352 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - sbv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// --- NetworkPolicyRule round-trip --- - -func TestNetworkPolicyRuleFromProto(t *testing.T) { - proto := &sbv1.NetworkPolicyRule{ - Name: "web-api", - Endpoints: []*sbv1.NetworkEndpoint{ - { - Host: "api.example.com", - Port: 443, - Protocol: "rest", - Tls: sbv1.NetworkTlsMode_NETWORK_TLS_MODE_SKIP, - Enforcement: sbv1.NetworkEnforcementMode_NETWORK_ENFORCEMENT_MODE_ENFORCE, - Access: sbv1.NetworkAccessPreset_NETWORK_ACCESS_PRESET_READ_ONLY, - Ports: []uint32{80, 443}, - AllowedIps: []string{"10.0.0.1", "10.0.0.2"}, - AllowEncodedSlash: true, - PersistedQueries: "allow", - GraphqlMaxBodyBytes: 1024, - Path: "/api/v1", - WebsocketCredentialRewrite: true, - RequestBodyCredentialRewrite: false, - AllowUninspectedCredentials: true, - ProviderCredentialed: true, - AdvisorProposed: true, - CredentialSigning: "sigv4", - SigningService: "bedrock", - SigningRegion: "us-west-2", - JsonRpcMaxBodyBytes: 65536, - Mcp: &sbv1.McpOptions{ - StrictToolNames: boolPtr(true), - AllowAllKnownMcpMethods: boolPtr(false), - }, - Rules: []*sbv1.L7Rule{ - { - Allow: &sbv1.L7Allow{ - Method: "GET", - Path: "/users", - Command: "list", - Query: map[string]*sbv1.L7QueryMatcher{ - "page": {Glob: "[0-9]*", Any: []string{"1", "2"}}, - }, - OperationType: "query", - OperationName: "GetUsers", - Fields: []string{"id", "name"}, - Params: map[string]*sbv1.L7QueryMatcher{ - "name": {Glob: "my-tool-*"}, - }, - }, - }, - }, - DenyRules: []*sbv1.L7DenyRule{ - { - Method: "DELETE", - Path: "/admin", - Command: "rm", - OperationType: "mutation", - OperationName: "DeleteAll", - Fields: []string{"*"}, - Query: map[string]*sbv1.L7QueryMatcher{ - "force": {Glob: "true"}, - }, - Params: map[string]*sbv1.L7QueryMatcher{ - "tool": {Glob: "deny-*"}, - }, - }, - }, - GraphqlPersistedQueries: map[string]*sbv1.GraphqlOperation{ - "abc123": { - OperationType: "query", - OperationName: "GetUser", - Fields: []string{"id", "email"}, - }, - }, - }, - }, - Binaries: []*sbv1.NetworkBinary{ - {Path: "/usr/bin/curl"}, - }, - } - - rule := NetworkPolicyRuleFromProto(proto) - - require.NotNil(t, rule) - assert.Equal(t, "web-api", rule.Name) - require.Len(t, rule.Endpoints, 1) - ep := rule.Endpoints[0] - assert.Equal(t, "api.example.com", ep.Host) - assert.Equal(t, uint32(443), ep.Port) - assert.Equal(t, "rest", ep.Protocol) - assert.Equal(t, v1.NetworkTLSModeSkip, ep.TLS) - assert.Equal(t, v1.NetworkEnforcementModeEnforce, ep.Enforcement) - assert.Equal(t, v1.NetworkAccessPresetReadOnly, ep.Access) - assert.Equal(t, []uint32{80, 443}, ep.Ports) - assert.Equal(t, []string{"10.0.0.1", "10.0.0.2"}, ep.AllowedIPs) - assert.True(t, ep.AllowEncodedSlash) - assert.Equal(t, "allow", ep.PersistedQueries) - assert.Equal(t, uint32(1024), ep.GraphqlMaxBodyBytes) - assert.Equal(t, "/api/v1", ep.Path) - assert.True(t, ep.WebsocketCredentialRewrite) - assert.False(t, ep.RequestBodyCredentialRewrite) - assert.True(t, ep.AllowUninspectedCredentials) - assert.True(t, ep.ProviderCredentialed) - assert.True(t, ep.AdvisorProposed) - assert.Equal(t, "sigv4", ep.CredentialSigning) - assert.Equal(t, "bedrock", ep.SigningService) - assert.Equal(t, "us-west-2", ep.SigningRegion) - assert.Equal(t, uint32(65536), ep.JSONRPCMaxBodyBytes) - - // MCP options - require.NotNil(t, ep.Mcp) - require.NotNil(t, ep.Mcp.StrictToolNames) - assert.True(t, *ep.Mcp.StrictToolNames) - require.NotNil(t, ep.Mcp.AllowAllKnownMcpMethods) - assert.False(t, *ep.Mcp.AllowAllKnownMcpMethods) - - // L7 rules - require.Len(t, ep.Rules, 1) - allow := ep.Rules[0].Allow - require.NotNil(t, allow) - assert.Equal(t, "GET", allow.Method) - assert.Equal(t, "/users", allow.Path) - assert.Equal(t, "list", allow.Command) - assert.Equal(t, "query", allow.OperationType) - assert.Equal(t, "GetUsers", allow.OperationName) - assert.Equal(t, []string{"id", "name"}, allow.Fields) - require.Contains(t, allow.Query, "page") - assert.Equal(t, "[0-9]*", allow.Query["page"].Glob) - assert.Equal(t, []string{"1", "2"}, allow.Query["page"].Any) - require.Contains(t, allow.Params, "name") - assert.Equal(t, "my-tool-*", allow.Params["name"].Glob) - - // Deny rules - require.Len(t, ep.DenyRules, 1) - deny := ep.DenyRules[0] - assert.Equal(t, "DELETE", deny.Method) - assert.Equal(t, "/admin", deny.Path) - assert.Equal(t, "rm", deny.Command) - assert.Equal(t, "mutation", deny.OperationType) - assert.Equal(t, "DeleteAll", deny.OperationName) - assert.Equal(t, []string{"*"}, deny.Fields) - require.Contains(t, deny.Query, "force") - assert.Equal(t, "true", deny.Query["force"].Glob) - require.Contains(t, deny.Params, "tool") - assert.Equal(t, "deny-*", deny.Params["tool"].Glob) - - // GraphQL persisted queries - require.Contains(t, ep.GraphqlPersistedQueries, "abc123") - gql := ep.GraphqlPersistedQueries["abc123"] - assert.Equal(t, "query", gql.OperationType) - assert.Equal(t, "GetUser", gql.OperationName) - assert.Equal(t, []string{"id", "email"}, gql.Fields) - - // Binaries - require.Len(t, rule.Binaries, 1) - assert.Equal(t, "/usr/bin/curl", rule.Binaries[0].Path) -} - -func TestNetworkPolicyRuleFromProto_Nil(t *testing.T) { - assert.Nil(t, NetworkPolicyRuleFromProto(nil)) -} - -func TestNetworkPolicyRuleRoundTrip(t *testing.T) { - original := &v1.NetworkPolicyRule{ - Name: "graphql-api", - Endpoints: []v1.PolicyNetworkEndpoint{ - { - Host: "gql.example.com", - Port: 8080, - Protocol: "graphql", - TLS: v1.NetworkTLSModeSkip, - Enforcement: v1.NetworkEnforcementModeAudit, - Access: v1.NetworkAccessPresetReadOnly, - Ports: []uint32{8080, 8443}, - AllowedIPs: []string{"192.168.1.0/24"}, - AllowEncodedSlash: false, - PersistedQueries: "enforce", - GraphqlMaxBodyBytes: 2048, - Path: "/graphql", - WebsocketCredentialRewrite: false, - RequestBodyCredentialRewrite: true, - AllowUninspectedCredentials: true, - ProviderCredentialed: true, - AdvisorProposed: false, - CredentialSigning: "sigv4", - SigningService: "bedrock", - SigningRegion: "us-east-1", - JSONRPCMaxBodyBytes: 32768, - Mcp: &v1.McpOptions{ - StrictToolNames: boolPtr(true), - AllowAllKnownMcpMethods: boolPtr(false), - }, - Rules: []v1.L7Rule{ - { - Allow: &v1.L7Allow{ - Method: "POST", - Path: "/graphql", - OperationType: "query", - OperationName: "ListItems", - Fields: []string{"id"}, - Query: map[string]v1.L7QueryMatcher{ - "limit": {Glob: "[0-9]+"}, - }, - Params: map[string]v1.L7QueryMatcher{ - "tool": {Glob: "allowed-*"}, - }, - }, - }, - }, - DenyRules: []v1.L7DenyRule{ - { - Method: "POST", - Path: "/graphql", - OperationType: "mutation", - OperationName: "DropDB", - Params: map[string]v1.L7QueryMatcher{ - "tool": {Glob: "denied-*"}, - }, - }, - }, - GraphqlPersistedQueries: map[string]v1.GraphqlOperation{ - "hash1": { - OperationType: "query", - OperationName: "Safe", - Fields: []string{"f1"}, - }, - }, - }, - }, - Binaries: []v1.PolicyNetworkBinary{ - {Path: "/usr/bin/wget"}, - }, - } - - proto := NetworkPolicyRuleToProto(original) - require.NotNil(t, proto) - - roundTrip := NetworkPolicyRuleFromProto(proto) - require.NotNil(t, roundTrip) - - assert.Equal(t, original.Name, roundTrip.Name) - require.Len(t, roundTrip.Endpoints, 1) - assert.Equal(t, original.Endpoints[0].Host, roundTrip.Endpoints[0].Host) - assert.Equal(t, original.Endpoints[0].Port, roundTrip.Endpoints[0].Port) - assert.Equal(t, original.Endpoints[0].Protocol, roundTrip.Endpoints[0].Protocol) - assert.Equal(t, original.Endpoints[0].TLS, roundTrip.Endpoints[0].TLS) - assert.Equal(t, original.Endpoints[0].Enforcement, roundTrip.Endpoints[0].Enforcement) - assert.Equal(t, original.Endpoints[0].Access, roundTrip.Endpoints[0].Access) - assert.Equal(t, original.Endpoints[0].Ports, roundTrip.Endpoints[0].Ports) - assert.Equal(t, original.Endpoints[0].AllowedIPs, roundTrip.Endpoints[0].AllowedIPs) - assert.Equal(t, original.Endpoints[0].AllowEncodedSlash, roundTrip.Endpoints[0].AllowEncodedSlash) - assert.Equal(t, original.Endpoints[0].GraphqlMaxBodyBytes, roundTrip.Endpoints[0].GraphqlMaxBodyBytes) - assert.Equal(t, original.Endpoints[0].AllowUninspectedCredentials, roundTrip.Endpoints[0].AllowUninspectedCredentials) - assert.Equal(t, original.Endpoints[0].ProviderCredentialed, roundTrip.Endpoints[0].ProviderCredentialed) - assert.Equal(t, original.Endpoints[0].AdvisorProposed, roundTrip.Endpoints[0].AdvisorProposed) - assert.Equal(t, original.Endpoints[0].CredentialSigning, roundTrip.Endpoints[0].CredentialSigning) - assert.Equal(t, original.Endpoints[0].SigningService, roundTrip.Endpoints[0].SigningService) - assert.Equal(t, original.Endpoints[0].SigningRegion, roundTrip.Endpoints[0].SigningRegion) - assert.Equal(t, original.Endpoints[0].JSONRPCMaxBodyBytes, roundTrip.Endpoints[0].JSONRPCMaxBodyBytes) - - // MCP round-trip - require.NotNil(t, roundTrip.Endpoints[0].Mcp) - assert.Equal(t, original.Endpoints[0].Mcp.StrictToolNames, roundTrip.Endpoints[0].Mcp.StrictToolNames) - assert.Equal(t, original.Endpoints[0].Mcp.AllowAllKnownMcpMethods, roundTrip.Endpoints[0].Mcp.AllowAllKnownMcpMethods) - - // L7 rules round-trip - require.Len(t, roundTrip.Endpoints[0].Rules, 1) - assert.Equal(t, original.Endpoints[0].Rules[0].Allow.Method, roundTrip.Endpoints[0].Rules[0].Allow.Method) - assert.Equal(t, original.Endpoints[0].Rules[0].Allow.OperationName, roundTrip.Endpoints[0].Rules[0].Allow.OperationName) - assert.Equal(t, original.Endpoints[0].Rules[0].Allow.Query["limit"].Glob, roundTrip.Endpoints[0].Rules[0].Allow.Query["limit"].Glob) - assert.Equal(t, original.Endpoints[0].Rules[0].Allow.Params["tool"].Glob, roundTrip.Endpoints[0].Rules[0].Allow.Params["tool"].Glob) - - // Deny rules round-trip - require.Len(t, roundTrip.Endpoints[0].DenyRules, 1) - assert.Equal(t, original.Endpoints[0].DenyRules[0].OperationName, roundTrip.Endpoints[0].DenyRules[0].OperationName) - assert.Equal(t, original.Endpoints[0].DenyRules[0].Params["tool"].Glob, roundTrip.Endpoints[0].DenyRules[0].Params["tool"].Glob) - - // GraphQL persisted queries round-trip - require.Contains(t, roundTrip.Endpoints[0].GraphqlPersistedQueries, "hash1") - - // Binaries round-trip - require.Len(t, roundTrip.Binaries, 1) - assert.Equal(t, original.Binaries[0].Path, roundTrip.Binaries[0].Path) -} - -func TestNetworkPolicyRuleToProto_Nil(t *testing.T) { - assert.Nil(t, NetworkPolicyRuleToProto(nil)) -} - -func TestNetworkPolicyRuleDeepCopy(t *testing.T) { - proto := &sbv1.NetworkPolicyRule{ - Name: "test", - Endpoints: []*sbv1.NetworkEndpoint{ - { - AllowedIps: []string{"1.2.3.4"}, - Ports: []uint32{80}, - Rules: []*sbv1.L7Rule{ - {Allow: &sbv1.L7Allow{Fields: []string{"f1"}}}, - }, - }, - }, - } - - rule := NetworkPolicyRuleFromProto(proto) - - // Mutate proto source - proto.Endpoints[0].AllowedIps[0] = "changed" - proto.Endpoints[0].Ports[0] = 9999 - proto.Endpoints[0].Rules[0].Allow.Fields[0] = "changed" - - // SDK type should be unaffected - assert.Equal(t, "1.2.3.4", rule.Endpoints[0].AllowedIPs[0]) - assert.Equal(t, uint32(80), rule.Endpoints[0].Ports[0]) - assert.Equal(t, "f1", rule.Endpoints[0].Rules[0].Allow.Fields[0]) - - // MCP deep copy - mcpProto := &sbv1.NetworkPolicyRule{ - Name: "mcp-test", - Endpoints: []*sbv1.NetworkEndpoint{ - { - Mcp: &sbv1.McpOptions{ - StrictToolNames: boolPtr(true), - }, - }, - }, - } - mcpRule := NetworkPolicyRuleFromProto(mcpProto) - *mcpProto.Endpoints[0].Mcp.StrictToolNames = false - require.NotNil(t, mcpRule.Endpoints[0].Mcp.StrictToolNames) - assert.True(t, *mcpRule.Endpoints[0].Mcp.StrictToolNames) -} - -func TestL7RuleFromProto_NilAllow(t *testing.T) { - proto := &sbv1.L7Rule{Allow: nil} - result := l7RuleFromProto(proto) - assert.Nil(t, result.Allow) -} - -func boolPtr(v bool) *bool { return &v } diff --git a/sdk/go/openshell/v1/internal/converter/policy.go b/sdk/go/openshell/v1/internal/converter/policy.go deleted file mode 100644 index 1a473654e9..0000000000 --- a/sdk/go/openshell/v1/internal/converter/policy.go +++ /dev/null @@ -1,391 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "fmt" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - sbv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - "google.golang.org/protobuf/types/known/structpb" -) - -// --- PolicyLoadStatus enum mapping --- - -// PolicyLoadStatusFromProto converts a proto PolicyStatus to an SDK PolicyLoadStatus. -func PolicyLoadStatusFromProto(s pb.PolicyStatus) types.PolicyLoadStatus { - switch s { - case pb.PolicyStatus_POLICY_STATUS_PENDING: - return types.PolicyLoadStatusPending - case pb.PolicyStatus_POLICY_STATUS_LOADED: - return types.PolicyLoadStatusLoaded - case pb.PolicyStatus_POLICY_STATUS_FAILED: - return types.PolicyLoadStatusFailed - case pb.PolicyStatus_POLICY_STATUS_SUPERSEDED: - return types.PolicyLoadStatusSuperseded - default: - return types.PolicyLoadStatusUnspecified - } -} - -// PolicyLoadStatusToProto converts an SDK PolicyLoadStatus to a proto PolicyStatus. -func PolicyLoadStatusToProto(s types.PolicyLoadStatus) pb.PolicyStatus { - switch s { - case types.PolicyLoadStatusPending: - return pb.PolicyStatus_POLICY_STATUS_PENDING - case types.PolicyLoadStatusLoaded: - return pb.PolicyStatus_POLICY_STATUS_LOADED - case types.PolicyLoadStatusFailed: - return pb.PolicyStatus_POLICY_STATUS_FAILED - case types.PolicyLoadStatusSuperseded: - return pb.PolicyStatus_POLICY_STATUS_SUPERSEDED - default: - return pb.PolicyStatus_POLICY_STATUS_UNSPECIFIED - } -} - -// --- PolicyChunk --- - -// PolicyChunkFromProto converts a proto PolicyChunk to an SDK PolicyChunk. -func PolicyChunkFromProto(c *pb.PolicyChunk) *types.PolicyChunk { - if c == nil { - return nil - } - return &types.PolicyChunk{ - ID: c.GetId(), - Status: c.GetStatus(), - RuleName: c.GetRuleName(), - ProposedRule: NetworkPolicyRuleFromProto(c.GetProposedRule()), - Rationale: c.GetRationale(), - SecurityNotes: c.GetSecurityNotes(), - Confidence: c.GetConfidence(), - DenialSummaryIDs: CopyStringSlice(c.GetDenialSummaryIds()), - CreatedAt: TimeFromProto(c.GetCreatedTime()), - DecidedAt: TimeFromProto(c.GetDecidedTime()), - Stage: c.GetStage(), - SupersedesChunkID: c.GetSupersedesChunkId(), - HitCount: c.GetHitCount(), - FirstSeen: TimeFromProto(c.GetFirstSeenTime()), - LastSeen: TimeFromProto(c.GetLastSeenTime()), - Binary: c.GetBinary(), - ValidationResult: c.GetValidationResult(), - RejectionReason: c.GetRejectionReason(), - ApplicationError: c.GetApplicationError(), - ReviewToken: c.GetReviewToken(), - CurrentEffectivePolicyHash: c.GetCurrentEffectivePolicyHash(), - CandidateEffectivePolicyHash: c.GetCandidateEffectivePolicyHash(), - CurrentEffectivePolicy: SandboxPolicyFromProto(c.GetCurrentEffectivePolicy()), - CandidateEffectivePolicy: SandboxPolicyFromProto(c.GetCandidateEffectivePolicy()), - } -} - -// --- DraftPolicy --- - -// DraftPolicyFromProto converts a proto GetDraftPolicyResponse to an SDK DraftPolicy. -func DraftPolicyFromProto(r *pb.GetDraftPolicyResponse) *types.DraftPolicy { - if r == nil { - return nil - } - result := &types.DraftPolicy{ - RollingSummary: r.GetRollingSummary(), - DraftVersion: r.GetDraftVersion(), - LastAnalyzedAt: TimeFromProto(r.GetLastAnalyzedTime()), - } - if chunks := r.GetChunks(); len(chunks) > 0 { - result.Chunks = make([]types.PolicyChunk, 0, len(chunks)) - for _, c := range chunks { - if converted := PolicyChunkFromProto(c); converted != nil { - result.Chunks = append(result.Chunks, *converted) - } - } - } - return result -} - -// --- SandboxPolicy --- - -// SandboxPolicyFromProto converts a proto SandboxPolicy to an SDK SandboxPolicy. -// Returns nil for nil input. All slice and map fields are deep-copied. -func SandboxPolicyFromProto(p *sbv1.SandboxPolicy) *types.SandboxPolicy { - if p == nil { - return nil - } - result := &types.SandboxPolicy{ - Version: p.GetVersion(), - Filesystem: filesystemPolicyFromProto(p.GetFilesystem()), - Landlock: landlockPolicyFromProto(p.GetLandlock()), - Process: processPolicyFromProto(p.GetProcess()), - } - if np := p.GetNetworkPolicies(); np != nil { - result.NetworkPolicies = make(map[string]types.NetworkPolicyRule, len(np)) - for k, v := range np { - if converted := NetworkPolicyRuleFromProto(v); converted != nil { - result.NetworkPolicies[k] = *converted - } - } - } - if mw := p.GetNetworkMiddlewares(); mw != nil { - result.NetworkMiddlewares = make(map[string]types.NetworkMiddlewareConfig, len(mw)) - for k, v := range mw { - if v != nil { - result.NetworkMiddlewares[k] = middlewareConfigFromProto(v) - } - } - } - return result -} - -// SandboxPolicyToProto converts an SDK SandboxPolicy to a proto SandboxPolicy. -// Returns nil for nil input. All slice and map fields are deep-copied. -func SandboxPolicyToProto(p *types.SandboxPolicy) *sbv1.SandboxPolicy { - if p == nil { - return nil - } - result := &sbv1.SandboxPolicy{ - Version: p.Version, - Filesystem: filesystemPolicyToProto(p.Filesystem), - Landlock: landlockPolicyToProto(p.Landlock), - Process: processPolicyToProto(p.Process), - } - if p.NetworkPolicies != nil { - result.NetworkPolicies = make(map[string]*sbv1.NetworkPolicyRule, len(p.NetworkPolicies)) - for k, v := range p.NetworkPolicies { - result.NetworkPolicies[k] = NetworkPolicyRuleToProto(&v) - } - } - if p.NetworkMiddlewares != nil { - result.NetworkMiddlewares = make(map[string]*sbv1.NetworkMiddlewareConfig, len(p.NetworkMiddlewares)) - for k, v := range p.NetworkMiddlewares { - result.NetworkMiddlewares[k] = middlewareConfigToProto(&v) - } - } - return result -} - -// SandboxPolicyToProtoChecked converts middleware configuration without -// silently discarding values unsupported by protobuf Struct. -func SandboxPolicyToProtoChecked(p *types.SandboxPolicy) (*sbv1.SandboxPolicy, error) { - result := SandboxPolicyToProto(p) - if p == nil { - return result, nil - } - for name, middleware := range p.NetworkMiddlewares { - if middleware.Config == nil { - continue - } - config, err := structpb.NewStruct(middleware.Config) - if err != nil { - return nil, fmt.Errorf("network middleware %q config: %w", name, err) - } - result.NetworkMiddlewares[name].Config = config - } - return result, nil -} - -func middlewareConfigFromProto(m *sbv1.NetworkMiddlewareConfig) types.NetworkMiddlewareConfig { - result := types.NetworkMiddlewareConfig{ - Name: m.GetName(), - Middleware: m.GetMiddleware(), - OnError: m.GetOnError(), - Order: m.GetOrder(), - } - if c := m.GetConfig(); c != nil { - result.Config = c.AsMap() - } - if ep := m.GetEndpoints(); ep != nil { - result.Endpoints = &types.MiddlewareEndpointSelector{ - Include: CopyStringSlice(ep.GetInclude()), - Exclude: CopyStringSlice(ep.GetExclude()), - } - } - return result -} - -func middlewareConfigToProto(m *types.NetworkMiddlewareConfig) *sbv1.NetworkMiddlewareConfig { - result := &sbv1.NetworkMiddlewareConfig{ - Name: m.Name, - Middleware: m.Middleware, - OnError: m.OnError, - Order: m.Order, - } - if m.Config != nil { - // Non-JSON-compatible values (e.g., chan, func) are silently dropped. - // Round-trip data from structpb.AsMap is always re-serializable. - s, err := structpb.NewStruct(m.Config) - if err == nil { - result.Config = s - } - } - if m.Endpoints != nil { - result.Endpoints = &sbv1.MiddlewareEndpointSelector{ - Include: CopyStringSlice(m.Endpoints.Include), - Exclude: CopyStringSlice(m.Endpoints.Exclude), - } - } - return result -} - -func filesystemPolicyFromProto(f *sbv1.FilesystemPolicy) *types.FilesystemPolicy { - if f == nil { - return nil - } - return &types.FilesystemPolicy{ - IncludeWorkdir: f.GetIncludeWorkdir(), - ReadOnly: CopyStringSlice(f.GetReadOnly()), - ReadWrite: CopyStringSlice(f.GetReadWrite()), - } -} - -func filesystemPolicyToProto(f *types.FilesystemPolicy) *sbv1.FilesystemPolicy { - if f == nil { - return nil - } - return &sbv1.FilesystemPolicy{ - IncludeWorkdir: f.IncludeWorkdir, - ReadOnly: CopyStringSlice(f.ReadOnly), - ReadWrite: CopyStringSlice(f.ReadWrite), - } -} - -func landlockPolicyFromProto(l *sbv1.LandlockPolicy) *types.LandlockPolicy { - if l == nil { - return nil - } - return &types.LandlockPolicy{ - Compatibility: l.GetCompatibility(), - } -} - -func landlockPolicyToProto(l *types.LandlockPolicy) *sbv1.LandlockPolicy { - if l == nil { - return nil - } - return &sbv1.LandlockPolicy{ - Compatibility: l.Compatibility, - } -} - -func processPolicyFromProto(p *sbv1.ProcessPolicy) *types.ProcessPolicy { - if p == nil { - return nil - } - return &types.ProcessPolicy{ - RunAsUser: p.GetRunAsUser(), - RunAsGroup: p.GetRunAsGroup(), - } -} - -func processPolicyToProto(p *types.ProcessPolicy) *sbv1.ProcessPolicy { - if p == nil { - return nil - } - return &sbv1.ProcessPolicy{ - RunAsUser: p.RunAsUser, - RunAsGroup: p.RunAsGroup, - } -} - -// --- SandboxPolicyRevision --- - -// SandboxPolicyRevisionFromProto converts a proto SandboxPolicyRevision to an SDK SandboxPolicyRevision. -func SandboxPolicyRevisionFromProto(r *pb.SandboxPolicyRevision) *types.SandboxPolicyRevision { - if r == nil { - return nil - } - return &types.SandboxPolicyRevision{ - Version: r.GetVersion(), - PolicyHash: r.GetPolicyHash(), - Status: PolicyLoadStatusFromProto(r.GetStatus()), - LoadError: r.GetLoadError(), - CreatedAt: TimeFromProto(r.GetCreatedTime()), - LoadedAt: TimeFromProto(r.GetLoadedTime()), - Policy: SandboxPolicyFromProto(r.GetPolicy()), - Provenance: CopyStringMap(r.GetProvenance()), - } -} - -// --- PolicyStatusResult --- - -// PolicyStatusResultFromProto converts a proto GetSandboxPolicyStatusResponse to an SDK PolicyStatusResult. -func PolicyStatusResultFromProto(r *pb.GetSandboxPolicyStatusResponse) *types.PolicyStatusResult { - if r == nil { - return nil - } - result := &types.PolicyStatusResult{ - ActiveVersion: r.GetActiveVersion(), - } - if rev := SandboxPolicyRevisionFromProto(r.GetRevision()); rev != nil { - result.Revision = *rev - } - return result -} - -// --- ApproveResult --- - -// ApproveResultFromProto converts a proto ApproveDraftChunkResponse to an SDK ApproveResult. -func ApproveResultFromProto(r *pb.ApproveDraftChunkResponse) *types.ApproveResult { - if r == nil { - return nil - } - return &types.ApproveResult{ - PolicyVersion: r.GetPolicyVersion(), - PolicyHash: r.GetPolicyHash(), - } -} - -// --- ApproveAllResult --- - -// ApproveAllResultFromProto converts a proto ApproveAllDraftChunksResponse to an SDK ApproveAllResult. -func ApproveAllResultFromProto(r *pb.ApproveAllDraftChunksResponse) *types.ApproveAllResult { - if r == nil { - return nil - } - return &types.ApproveAllResult{ - PolicyVersion: r.GetPolicyVersion(), - PolicyHash: r.GetPolicyHash(), - ChunksApproved: r.GetChunksApproved(), - ChunksSkipped: r.GetChunksSkipped(), - } -} - -// --- UndoResult --- - -// UndoResultFromProto converts a proto UndoDraftChunkResponse to an SDK UndoResult. -func UndoResultFromProto(r *pb.UndoDraftChunkResponse) *types.UndoResult { - if r == nil { - return nil - } - return &types.UndoResult{ - PolicyVersion: r.GetPolicyVersion(), - PolicyHash: r.GetPolicyHash(), - } -} - -// --- ClearResult --- - -// ClearResultFromProto converts a proto ClearDraftChunksResponse to an SDK ClearResult. -func ClearResultFromProto(r *pb.ClearDraftChunksResponse) *types.ClearResult { - if r == nil { - return nil - } - return &types.ClearResult{ - ChunksCleared: r.GetChunksCleared(), - } -} - -// --- DraftHistoryEntry --- - -// DraftHistoryEntryFromProto converts a proto DraftHistoryEntry to an SDK DraftHistoryEntry. -func DraftHistoryEntryFromProto(e *pb.DraftHistoryEntry) *types.DraftHistoryEntry { - if e == nil { - return nil - } - return &types.DraftHistoryEntry{ - Timestamp: TimeFromProto(e.GetEventTime()), - EventType: e.GetEventType(), - Description: e.GetDescription(), - ChunkID: e.GetChunkId(), - } -} diff --git a/sdk/go/openshell/v1/internal/converter/policy_test.go b/sdk/go/openshell/v1/internal/converter/policy_test.go deleted file mode 100644 index dde2205773..0000000000 --- a/sdk/go/openshell/v1/internal/converter/policy_test.go +++ /dev/null @@ -1,709 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - sbv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/types/known/structpb" -) - -// --- PolicyLoadStatus --- - -func TestPolicyLoadStatusFromProto(t *testing.T) { - tests := []struct { - proto pb.PolicyStatus - want v1.PolicyLoadStatus - }{ - {pb.PolicyStatus_POLICY_STATUS_UNSPECIFIED, v1.PolicyLoadStatusUnspecified}, - {pb.PolicyStatus_POLICY_STATUS_PENDING, v1.PolicyLoadStatusPending}, - {pb.PolicyStatus_POLICY_STATUS_LOADED, v1.PolicyLoadStatusLoaded}, - {pb.PolicyStatus_POLICY_STATUS_FAILED, v1.PolicyLoadStatusFailed}, - {pb.PolicyStatus_POLICY_STATUS_SUPERSEDED, v1.PolicyLoadStatusSuperseded}, - } - for _, tt := range tests { - t.Run(tt.proto.String(), func(t *testing.T) { - assert.Equal(t, tt.want, PolicyLoadStatusFromProto(tt.proto)) - }) - } -} - -func TestPolicyLoadStatusToProto(t *testing.T) { - tests := []struct { - sdk v1.PolicyLoadStatus - want pb.PolicyStatus - }{ - {v1.PolicyLoadStatusUnspecified, pb.PolicyStatus_POLICY_STATUS_UNSPECIFIED}, - {v1.PolicyLoadStatusPending, pb.PolicyStatus_POLICY_STATUS_PENDING}, - {v1.PolicyLoadStatusLoaded, pb.PolicyStatus_POLICY_STATUS_LOADED}, - {v1.PolicyLoadStatusFailed, pb.PolicyStatus_POLICY_STATUS_FAILED}, - {v1.PolicyLoadStatusSuperseded, pb.PolicyStatus_POLICY_STATUS_SUPERSEDED}, - } - for _, tt := range tests { - t.Run(tt.sdk.String(), func(t *testing.T) { - assert.Equal(t, tt.want, PolicyLoadStatusToProto(tt.sdk)) - }) - } -} - -func TestPolicyLoadStatusRoundTrip(t *testing.T) { - for _, s := range []v1.PolicyLoadStatus{ - v1.PolicyLoadStatusUnspecified, - v1.PolicyLoadStatusPending, - v1.PolicyLoadStatusLoaded, - v1.PolicyLoadStatusFailed, - v1.PolicyLoadStatusSuperseded, - } { - assert.Equal(t, s, PolicyLoadStatusFromProto(PolicyLoadStatusToProto(s))) - } -} - -// --- PolicyChunk --- - -func TestPolicyChunkFromProto(t *testing.T) { - proto := &pb.PolicyChunk{ - Id: "chunk-1", - Status: "pending", - RuleName: "web-api", - Rationale: "Observed DNS resolution", - SecurityNotes: "No concerns", - Confidence: 0.95, - DenialSummaryIds: []string{"d1", "d2"}, - CreatedTime: TimestampFromMillis(1700000000000), - DecidedTime: TimestampFromMillis(1700000001000), - Stage: "initial", - SupersedesChunkId: "chunk-0", - HitCount: 5, - FirstSeenTime: TimestampFromMillis(1699999999000), - LastSeenTime: TimestampFromMillis(1700000000500), - Binary: "/usr/bin/curl", - ValidationResult: "valid", - RejectionReason: "", - ProposedRule: &sbv1.NetworkPolicyRule{ - Name: "web-api", - Endpoints: []*sbv1.NetworkEndpoint{ - {Host: "api.example.com", Port: 443, Protocol: "rest"}, - }, - }, - } - - chunk := PolicyChunkFromProto(proto) - - require.NotNil(t, chunk) - assert.Equal(t, "chunk-1", chunk.ID) - assert.Equal(t, "pending", chunk.Status) - assert.Equal(t, "web-api", chunk.RuleName) - assert.Equal(t, "Observed DNS resolution", chunk.Rationale) - assert.Equal(t, "No concerns", chunk.SecurityNotes) - assert.InDelta(t, float32(0.95), chunk.Confidence, 0.001) - assert.Equal(t, []string{"d1", "d2"}, chunk.DenialSummaryIDs) - assert.False(t, chunk.CreatedAt.IsZero()) - assert.False(t, chunk.DecidedAt.IsZero()) - assert.Equal(t, "initial", chunk.Stage) - assert.Equal(t, "chunk-0", chunk.SupersedesChunkID) - assert.Equal(t, int32(5), chunk.HitCount) - assert.False(t, chunk.FirstSeen.IsZero()) - assert.False(t, chunk.LastSeen.IsZero()) - assert.Equal(t, "/usr/bin/curl", chunk.Binary) - assert.Equal(t, "valid", chunk.ValidationResult) - assert.Empty(t, chunk.RejectionReason) - - require.NotNil(t, chunk.ProposedRule) - assert.Equal(t, "web-api", chunk.ProposedRule.Name) - require.Len(t, chunk.ProposedRule.Endpoints, 1) - assert.Equal(t, "api.example.com", chunk.ProposedRule.Endpoints[0].Host) -} - -func TestPolicyChunkFromProto_Nil(t *testing.T) { - assert.Nil(t, PolicyChunkFromProto(nil)) -} - -func TestPolicyChunkDeepCopy(t *testing.T) { - proto := &pb.PolicyChunk{ - Id: "c1", - DenialSummaryIds: []string{"d1"}, - } - - chunk := PolicyChunkFromProto(proto) - proto.DenialSummaryIds[0] = "changed" - - assert.Equal(t, "d1", chunk.DenialSummaryIDs[0]) -} - -// --- DraftPolicy --- - -func TestDraftPolicyFromProto(t *testing.T) { - proto := &pb.GetDraftPolicyResponse{ - Chunks: []*pb.PolicyChunk{ - {Id: "c1", Status: "pending", RuleName: "rule1"}, - {Id: "c2", Status: "approved", RuleName: "rule2"}, - }, - RollingSummary: "Analysis summary", - DraftVersion: 42, - LastAnalyzedTime: TimestampFromMillis(1700000000000), - } - - draft := DraftPolicyFromProto(proto) - - require.NotNil(t, draft) - assert.Len(t, draft.Chunks, 2) - assert.Equal(t, "c1", draft.Chunks[0].ID) - assert.Equal(t, "c2", draft.Chunks[1].ID) - assert.Equal(t, "Analysis summary", draft.RollingSummary) - assert.Equal(t, uint64(42), draft.DraftVersion) - assert.False(t, draft.LastAnalyzedAt.IsZero()) -} - -func TestDraftPolicyFromProto_Nil(t *testing.T) { - assert.Nil(t, DraftPolicyFromProto(nil)) -} - -func TestDraftPolicyFromProto_EmptyChunks(t *testing.T) { - proto := &pb.GetDraftPolicyResponse{ - RollingSummary: "empty", - DraftVersion: 1, - } - - draft := DraftPolicyFromProto(proto) - require.NotNil(t, draft) - assert.Empty(t, draft.Chunks) -} - -// --- SandboxPolicy --- - -func TestSandboxPolicyFromProtoNil(t *testing.T) { - assert.Nil(t, SandboxPolicyFromProto(nil)) -} - -func TestSandboxPolicyToProtoNil(t *testing.T) { - assert.Nil(t, SandboxPolicyToProto(nil)) -} - -func TestSandboxPolicyRoundTrip(t *testing.T) { - original := &v1.SandboxPolicy{ - Version: 5, - Filesystem: &v1.FilesystemPolicy{ - IncludeWorkdir: true, - ReadOnly: []string{"/etc", "/usr/share"}, - ReadWrite: []string{"/tmp", "/workspace"}, - }, - Landlock: &v1.LandlockPolicy{ - Compatibility: "best_effort", - }, - Process: &v1.ProcessPolicy{ - RunAsUser: "sandbox-user", - RunAsGroup: "sandbox-group", - }, - NetworkPolicies: map[string]v1.NetworkPolicyRule{ - "web-api": { - Name: "web-api", - Endpoints: []v1.PolicyNetworkEndpoint{ - {Host: "api.example.com", Port: 443, Protocol: "rest"}, - }, - }, - "db": { - Name: "db", - Endpoints: []v1.PolicyNetworkEndpoint{ - {Host: "db.internal", Port: 5432, Protocol: "tcp"}, - }, - }, - }, - } - - proto := SandboxPolicyToProto(original) - require.NotNil(t, proto) - - roundTrip := SandboxPolicyFromProto(proto) - require.NotNil(t, roundTrip) - - assert.Equal(t, original.Version, roundTrip.Version) - - // Filesystem - require.NotNil(t, roundTrip.Filesystem) - assert.Equal(t, original.Filesystem.IncludeWorkdir, roundTrip.Filesystem.IncludeWorkdir) - assert.Equal(t, original.Filesystem.ReadOnly, roundTrip.Filesystem.ReadOnly) - assert.Equal(t, original.Filesystem.ReadWrite, roundTrip.Filesystem.ReadWrite) - - // Landlock - require.NotNil(t, roundTrip.Landlock) - assert.Equal(t, original.Landlock.Compatibility, roundTrip.Landlock.Compatibility) - - // Process - require.NotNil(t, roundTrip.Process) - assert.Equal(t, original.Process.RunAsUser, roundTrip.Process.RunAsUser) - assert.Equal(t, original.Process.RunAsGroup, roundTrip.Process.RunAsGroup) - - // NetworkPolicies - require.Len(t, roundTrip.NetworkPolicies, 2) - webAPI, ok := roundTrip.NetworkPolicies["web-api"] - require.True(t, ok) - assert.Equal(t, "web-api", webAPI.Name) - require.Len(t, webAPI.Endpoints, 1) - assert.Equal(t, "api.example.com", webAPI.Endpoints[0].Host) - - db, ok := roundTrip.NetworkPolicies["db"] - require.True(t, ok) - assert.Equal(t, "db", db.Name) -} - -func TestSandboxPolicyDeepCopy(t *testing.T) { - // Build a proto, convert to SDK, mutate proto, verify SDK is isolated. - proto := &sbv1.SandboxPolicy{ - Version: 1, - Filesystem: &sbv1.FilesystemPolicy{ - IncludeWorkdir: true, - ReadOnly: []string{"/original"}, - ReadWrite: []string{"/tmp"}, - }, - NetworkPolicies: map[string]*sbv1.NetworkPolicyRule{ - "rule1": { - Name: "rule1", - Endpoints: []*sbv1.NetworkEndpoint{ - {Host: "original.host", Port: 80}, - }, - }, - }, - } - - sdk := SandboxPolicyFromProto(proto) - require.NotNil(t, sdk) - - // Mutate proto source after conversion. - proto.Version = 99 - proto.Filesystem.ReadOnly[0] = "mutated" - proto.Filesystem.ReadWrite[0] = "mutated" - proto.NetworkPolicies["rule1"].Name = "mutated" - proto.NetworkPolicies["rule1"].Endpoints[0].Host = "mutated.host" - - // SDK values must be unaffected. - assert.Equal(t, uint32(1), sdk.Version) - assert.Equal(t, "/original", sdk.Filesystem.ReadOnly[0]) - assert.Equal(t, "/tmp", sdk.Filesystem.ReadWrite[0]) - assert.Equal(t, "rule1", sdk.NetworkPolicies["rule1"].Name) - assert.Equal(t, "original.host", sdk.NetworkPolicies["rule1"].Endpoints[0].Host) - - // Also test ToProto deep-copy isolation. - protoOut := SandboxPolicyToProto(sdk) - require.NotNil(t, protoOut) - - // Mutate SDK after ToProto conversion. - sdk.Filesystem.ReadOnly[0] = "sdk-mutated" - - // Proto output must be unaffected. - assert.Equal(t, "/original", protoOut.Filesystem.ReadOnly[0]) -} - -func TestSandboxPolicyPartialSubPolicies(t *testing.T) { - t.Run("only filesystem", func(t *testing.T) { - original := &v1.SandboxPolicy{ - Version: 1, - Filesystem: &v1.FilesystemPolicy{ - ReadOnly: []string{"/etc"}, - }, - } - roundTrip := SandboxPolicyFromProto(SandboxPolicyToProto(original)) - require.NotNil(t, roundTrip) - require.NotNil(t, roundTrip.Filesystem) - assert.Nil(t, roundTrip.Landlock) - assert.Nil(t, roundTrip.Process) - assert.Nil(t, roundTrip.NetworkPolicies) - }) - - t.Run("only landlock", func(t *testing.T) { - original := &v1.SandboxPolicy{ - Version: 2, - Landlock: &v1.LandlockPolicy{ - Compatibility: "hard_requirement", - }, - } - roundTrip := SandboxPolicyFromProto(SandboxPolicyToProto(original)) - require.NotNil(t, roundTrip) - assert.Nil(t, roundTrip.Filesystem) - require.NotNil(t, roundTrip.Landlock) - assert.Equal(t, "hard_requirement", roundTrip.Landlock.Compatibility) - assert.Nil(t, roundTrip.Process) - assert.Nil(t, roundTrip.NetworkPolicies) - }) - - t.Run("only process", func(t *testing.T) { - original := &v1.SandboxPolicy{ - Process: &v1.ProcessPolicy{ - RunAsUser: "nobody", - }, - } - roundTrip := SandboxPolicyFromProto(SandboxPolicyToProto(original)) - require.NotNil(t, roundTrip) - assert.Nil(t, roundTrip.Filesystem) - assert.Nil(t, roundTrip.Landlock) - require.NotNil(t, roundTrip.Process) - assert.Equal(t, "nobody", roundTrip.Process.RunAsUser) - }) - - t.Run("only network policies", func(t *testing.T) { - original := &v1.SandboxPolicy{ - NetworkPolicies: map[string]v1.NetworkPolicyRule{ - "r1": {Name: "r1"}, - }, - } - roundTrip := SandboxPolicyFromProto(SandboxPolicyToProto(original)) - require.NotNil(t, roundTrip) - assert.Nil(t, roundTrip.Filesystem) - assert.Nil(t, roundTrip.Landlock) - assert.Nil(t, roundTrip.Process) - require.Len(t, roundTrip.NetworkPolicies, 1) - }) - - t.Run("empty network policies map preserved", func(t *testing.T) { - proto := &sbv1.SandboxPolicy{ - NetworkPolicies: map[string]*sbv1.NetworkPolicyRule{}, - } - // Proto empty map is non-nil, so converter creates an empty SDK map. - sdk := SandboxPolicyFromProto(proto) - require.NotNil(t, sdk) - require.NotNil(t, sdk.NetworkPolicies) - assert.Empty(t, sdk.NetworkPolicies) - }) -} - -func TestFilesystemPolicyRoundTrip(t *testing.T) { - original := &v1.FilesystemPolicy{ - IncludeWorkdir: true, - ReadOnly: []string{"/etc", "/usr/lib"}, - ReadWrite: []string{"/tmp", "/var/run"}, - } - - proto := filesystemPolicyToProto(original) - require.NotNil(t, proto) - - roundTrip := filesystemPolicyFromProto(proto) - require.NotNil(t, roundTrip) - - assert.Equal(t, original.IncludeWorkdir, roundTrip.IncludeWorkdir) - assert.Equal(t, original.ReadOnly, roundTrip.ReadOnly) - assert.Equal(t, original.ReadWrite, roundTrip.ReadWrite) -} - -func TestFilesystemPolicyNil(t *testing.T) { - assert.Nil(t, filesystemPolicyFromProto(nil)) - assert.Nil(t, filesystemPolicyToProto(nil)) -} - -func TestLandlockPolicyRoundTrip(t *testing.T) { - original := &v1.LandlockPolicy{ - Compatibility: "best_effort", - } - - proto := landlockPolicyToProto(original) - require.NotNil(t, proto) - - roundTrip := landlockPolicyFromProto(proto) - require.NotNil(t, roundTrip) - - assert.Equal(t, original.Compatibility, roundTrip.Compatibility) -} - -func TestLandlockPolicyNil(t *testing.T) { - assert.Nil(t, landlockPolicyFromProto(nil)) - assert.Nil(t, landlockPolicyToProto(nil)) -} - -func TestProcessPolicyRoundTrip(t *testing.T) { - original := &v1.ProcessPolicy{ - RunAsUser: "app-user", - RunAsGroup: "app-group", - } - - proto := processPolicyToProto(original) - require.NotNil(t, proto) - - roundTrip := processPolicyFromProto(proto) - require.NotNil(t, roundTrip) - - assert.Equal(t, original.RunAsUser, roundTrip.RunAsUser) - assert.Equal(t, original.RunAsGroup, roundTrip.RunAsGroup) -} - -func TestProcessPolicyNil(t *testing.T) { - assert.Nil(t, processPolicyFromProto(nil)) - assert.Nil(t, processPolicyToProto(nil)) -} - -// --- SandboxPolicyRevision --- - -func TestSandboxPolicyRevisionFromProto(t *testing.T) { - proto := &pb.SandboxPolicyRevision{ - Version: 3, - PolicyHash: "sha256:abc123", - Status: pb.PolicyStatus_POLICY_STATUS_LOADED, - LoadError: "", - CreatedTime: TimestampFromMillis(1700000000000), - LoadedTime: TimestampFromMillis(1700000001000), - Provenance: map[string]string{"source": "api", "user": "admin"}, - } - - rev := SandboxPolicyRevisionFromProto(proto) - - require.NotNil(t, rev) - assert.Equal(t, uint32(3), rev.Version) - assert.Equal(t, "sha256:abc123", rev.PolicyHash) - assert.Equal(t, v1.PolicyLoadStatusLoaded, rev.Status) - assert.Empty(t, rev.LoadError) - assert.False(t, rev.CreatedAt.IsZero()) - assert.False(t, rev.LoadedAt.IsZero()) - assert.Equal(t, map[string]string{"source": "api", "user": "admin"}, rev.Provenance) - - proto.Provenance["source"] = "MUTATED" - assert.Equal(t, "api", rev.Provenance["source"], "provenance must be deep copied") -} - -func TestSandboxPolicyRevisionFromProto_Nil(t *testing.T) { - assert.Nil(t, SandboxPolicyRevisionFromProto(nil)) -} - -func TestSandboxPolicyRevisionFromProto_WithPolicy(t *testing.T) { - proto := &pb.SandboxPolicyRevision{ - Version: 1, - PolicyHash: "sha256:def", - Status: pb.PolicyStatus_POLICY_STATUS_LOADED, - Policy: &sbv1.SandboxPolicy{ - Version: 2, - Filesystem: &sbv1.FilesystemPolicy{ - ReadOnly: []string{"/etc"}, - }, - }, - } - - rev := SandboxPolicyRevisionFromProto(proto) - require.NotNil(t, rev) - require.NotNil(t, rev.Policy, "typed SandboxPolicy should be populated when proto policy is set") - assert.Equal(t, uint32(2), rev.Policy.Version) - require.NotNil(t, rev.Policy.Filesystem) - assert.Equal(t, []string{"/etc"}, rev.Policy.Filesystem.ReadOnly) -} - -// --- PolicyStatusResult --- - -func TestPolicyStatusResultFromProto(t *testing.T) { - proto := &pb.GetSandboxPolicyStatusResponse{ - Revision: &pb.SandboxPolicyRevision{ - Version: 5, - PolicyHash: "sha256:xyz", - Status: pb.PolicyStatus_POLICY_STATUS_PENDING, - }, - ActiveVersion: 4, - } - - result := PolicyStatusResultFromProto(proto) - - require.NotNil(t, result) - assert.Equal(t, uint32(5), result.Revision.Version) - assert.Equal(t, "sha256:xyz", result.Revision.PolicyHash) - assert.Equal(t, v1.PolicyLoadStatusPending, result.Revision.Status) - assert.Equal(t, uint32(4), result.ActiveVersion) -} - -func TestPolicyStatusResultFromProto_Nil(t *testing.T) { - assert.Nil(t, PolicyStatusResultFromProto(nil)) -} - -// --- ApproveResult --- - -func TestApproveResultFromProto(t *testing.T) { - proto := &pb.ApproveDraftChunkResponse{ - PolicyVersion: 7, - PolicyHash: "sha256:merged", - } - - result := ApproveResultFromProto(proto) - - require.NotNil(t, result) - assert.Equal(t, uint32(7), result.PolicyVersion) - assert.Equal(t, "sha256:merged", result.PolicyHash) -} - -func TestApproveResultFromProto_Nil(t *testing.T) { - assert.Nil(t, ApproveResultFromProto(nil)) -} - -// --- ApproveAllResult --- - -func TestApproveAllResultFromProto(t *testing.T) { - proto := &pb.ApproveAllDraftChunksResponse{ - PolicyVersion: 8, - PolicyHash: "sha256:all", - ChunksApproved: 10, - ChunksSkipped: 2, - } - - result := ApproveAllResultFromProto(proto) - - require.NotNil(t, result) - assert.Equal(t, uint32(8), result.PolicyVersion) - assert.Equal(t, "sha256:all", result.PolicyHash) - assert.Equal(t, uint32(10), result.ChunksApproved) - assert.Equal(t, uint32(2), result.ChunksSkipped) -} - -func TestApproveAllResultFromProto_Nil(t *testing.T) { - assert.Nil(t, ApproveAllResultFromProto(nil)) -} - -// --- UndoResult --- - -func TestUndoResultFromProto(t *testing.T) { - proto := &pb.UndoDraftChunkResponse{ - PolicyVersion: 6, - PolicyHash: "sha256:reverted", - } - - result := UndoResultFromProto(proto) - - require.NotNil(t, result) - assert.Equal(t, uint32(6), result.PolicyVersion) - assert.Equal(t, "sha256:reverted", result.PolicyHash) -} - -func TestUndoResultFromProto_Nil(t *testing.T) { - assert.Nil(t, UndoResultFromProto(nil)) -} - -// --- ClearResult --- - -func TestClearResultFromProto(t *testing.T) { - proto := &pb.ClearDraftChunksResponse{ - ChunksCleared: 15, - } - - result := ClearResultFromProto(proto) - - require.NotNil(t, result) - assert.Equal(t, uint32(15), result.ChunksCleared) -} - -func TestClearResultFromProto_Nil(t *testing.T) { - assert.Nil(t, ClearResultFromProto(nil)) -} - -// --- DraftHistoryEntry --- - -func TestDraftHistoryEntryFromProto(t *testing.T) { - proto := &pb.DraftHistoryEntry{ - EventTime: TimestampFromMillis(1700000000000), - EventType: "approved", - Description: "Chunk c1 approved", - ChunkId: "c1", - } - - entry := DraftHistoryEntryFromProto(proto) - - require.NotNil(t, entry) - assert.False(t, entry.Timestamp.IsZero()) - assert.Equal(t, "approved", entry.EventType) - assert.Equal(t, "Chunk c1 approved", entry.Description) - assert.Equal(t, "c1", entry.ChunkID) -} - -func TestDraftHistoryEntryFromProto_Nil(t *testing.T) { - assert.Nil(t, DraftHistoryEntryFromProto(nil)) -} - -// --- NetworkMiddleware --- - -func TestSandboxPolicyFromProto_WithMiddleware(t *testing.T) { - proto := &sbv1.SandboxPolicy{ - Version: 3, - NetworkMiddlewares: map[string]*sbv1.NetworkMiddlewareConfig{ - "sigv4-rewriter": { - Name: "sigv4-rewriter", - Middleware: "aws-sigv4", - OnError: "fail_closed", - Order: 10, - Config: func() *structpb.Struct { - s, _ := structpb.NewStruct(map[string]any{ - "region": "us-east-1", - "service": "bedrock", - }) - return s - }(), - Endpoints: &sbv1.MiddlewareEndpointSelector{ - Include: []string{"*.bedrock.amazonaws.com"}, - Exclude: []string{"sts.amazonaws.com"}, - }, - }, - }, - } - - policy := SandboxPolicyFromProto(proto) - - require.NotNil(t, policy) - require.Contains(t, policy.NetworkMiddlewares, "sigv4-rewriter") - mw := policy.NetworkMiddlewares["sigv4-rewriter"] - assert.Equal(t, "sigv4-rewriter", mw.Name) - assert.Equal(t, "aws-sigv4", mw.Middleware) - assert.Equal(t, "fail_closed", mw.OnError) - assert.Equal(t, int32(10), mw.Order) - require.NotNil(t, mw.Config) - assert.Equal(t, "us-east-1", mw.Config["region"]) - assert.Equal(t, "bedrock", mw.Config["service"]) - require.NotNil(t, mw.Endpoints) - assert.Equal(t, []string{"*.bedrock.amazonaws.com"}, mw.Endpoints.Include) - assert.Equal(t, []string{"sts.amazonaws.com"}, mw.Endpoints.Exclude) -} - -func TestSandboxPolicyMiddlewareRoundTrip(t *testing.T) { - original := &v1.SandboxPolicy{ - Version: 5, - NetworkMiddlewares: map[string]v1.NetworkMiddlewareConfig{ - "rate-limiter": { - Name: "rate-limiter", - Middleware: "envoy-ratelimit", - OnError: "fail_open", - Order: 20, - Config: map[string]any{ - "requests_per_second": float64(100), - }, - Endpoints: &v1.MiddlewareEndpointSelector{ - Include: []string{"api.*"}, - }, - }, - }, - } - - proto := SandboxPolicyToProto(original) - require.NotNil(t, proto) - - roundTrip := SandboxPolicyFromProto(proto) - require.NotNil(t, roundTrip) - - require.Contains(t, roundTrip.NetworkMiddlewares, "rate-limiter") - mw := roundTrip.NetworkMiddlewares["rate-limiter"] - assert.Equal(t, original.NetworkMiddlewares["rate-limiter"].Name, mw.Name) - assert.Equal(t, original.NetworkMiddlewares["rate-limiter"].Middleware, mw.Middleware) - assert.Equal(t, original.NetworkMiddlewares["rate-limiter"].OnError, mw.OnError) - assert.Equal(t, original.NetworkMiddlewares["rate-limiter"].Order, mw.Order) - assert.Equal(t, original.NetworkMiddlewares["rate-limiter"].Config["requests_per_second"], mw.Config["requests_per_second"]) - assert.Equal(t, original.NetworkMiddlewares["rate-limiter"].Endpoints.Include, mw.Endpoints.Include) -} - -func TestSandboxPolicyMiddlewareDeepCopy(t *testing.T) { - proto := &sbv1.SandboxPolicy{ - NetworkMiddlewares: map[string]*sbv1.NetworkMiddlewareConfig{ - "test": { - Endpoints: &sbv1.MiddlewareEndpointSelector{ - Include: []string{"original.com"}, - }, - }, - }, - } - - policy := SandboxPolicyFromProto(proto) - proto.NetworkMiddlewares["test"].Endpoints.Include[0] = "mutated.com" - - assert.Equal(t, "original.com", policy.NetworkMiddlewares["test"].Endpoints.Include[0]) -} diff --git a/sdk/go/openshell/v1/internal/converter/profile.go b/sdk/go/openshell/v1/internal/converter/profile.go deleted file mode 100644 index b3a5ee88c8..0000000000 --- a/sdk/go/openshell/v1/internal/converter/profile.go +++ /dev/null @@ -1,500 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - sbv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - "google.golang.org/protobuf/types/known/durationpb" -) - -// --- ProfileCategory enum mapping --- - -// ProfileCategoryFromProto converts a proto ProviderProfileCategory to an SDK ProfileCategory. -func ProfileCategoryFromProto(c pb.ProviderProfileCategory) types.ProfileCategory { - switch c { - case pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_OTHER: - return types.ProfileCategoryOther - case pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE: - return types.ProfileCategoryInference - case pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_AGENT: - return types.ProfileCategoryAgent - case pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL: - return types.ProfileCategorySourceControl - case pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_MESSAGING: - return types.ProfileCategoryMessaging - case pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_DATA: - return types.ProfileCategoryData - case pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_KNOWLEDGE: - return types.ProfileCategoryKnowledge - default: - return types.ProfileCategory("") - } -} - -// ProfileCategoryToProto converts an SDK ProfileCategory to a proto ProviderProfileCategory. -func ProfileCategoryToProto(c types.ProfileCategory) pb.ProviderProfileCategory { - switch c { - case types.ProfileCategoryOther: - return pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_OTHER - case types.ProfileCategoryInference: - return pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE - case types.ProfileCategoryAgent: - return pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_AGENT - case types.ProfileCategorySourceControl: - return pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL - case types.ProfileCategoryMessaging: - return pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_MESSAGING - case types.ProfileCategoryData: - return pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_DATA - case types.ProfileCategoryKnowledge: - return pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_KNOWLEDGE - default: - return pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_UNSPECIFIED - } -} - -// CredentialTokenGrantTypeFromProto converts a proto token grant type to an SDK token grant type. -func CredentialTokenGrantTypeFromProto(t pb.ProviderCredentialTokenGrantType) types.CredentialTokenGrantType { - switch t { - case pb.ProviderCredentialTokenGrantType_PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_CLIENT_CREDENTIALS: - return types.CredentialTokenGrantTypeClientCredentials - case pb.ProviderCredentialTokenGrantType_PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_TOKEN_EXCHANGE: - return types.CredentialTokenGrantTypeTokenExchange - default: - return types.CredentialTokenGrantType("") - } -} - -// CredentialTokenGrantTypeToProto converts an SDK token grant type to a proto token grant type. -func CredentialTokenGrantTypeToProto(t types.CredentialTokenGrantType) pb.ProviderCredentialTokenGrantType { - switch t { - case types.CredentialTokenGrantTypeClientCredentials: - return pb.ProviderCredentialTokenGrantType_PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_CLIENT_CREDENTIALS - case types.CredentialTokenGrantTypeTokenExchange: - return pb.ProviderCredentialTokenGrantType_PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_TOKEN_EXCHANGE - default: - return pb.ProviderCredentialTokenGrantType_PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_UNSPECIFIED - } -} - -// --- NetworkEndpoint --- - -// NetworkEndpointFromProto converts a proto NetworkEndpoint to an SDK NetworkEndpoint. -// Only Host, Port, and Protocol are mapped; additional proto fields are ignored. -func NetworkEndpointFromProto(ep *sbv1.NetworkEndpoint) *types.NetworkEndpoint { - if ep == nil { - return nil - } - return &types.NetworkEndpoint{ - Host: ep.GetHost(), - Port: ep.GetPort(), - Protocol: ep.GetProtocol(), - } -} - -// NetworkEndpointToProto converts an SDK NetworkEndpoint to a proto NetworkEndpoint. -func NetworkEndpointToProto(ep *types.NetworkEndpoint) *sbv1.NetworkEndpoint { - if ep == nil { - return nil - } - return &sbv1.NetworkEndpoint{ - Host: ep.Host, - Port: ep.Port, - Protocol: ep.Protocol, - } -} - -// --- NetworkBinary --- - -// NetworkBinaryFromProto converts a proto NetworkBinary to an SDK NetworkBinary. -func NetworkBinaryFromProto(b *sbv1.NetworkBinary) *types.NetworkBinary { - if b == nil { - return nil - } - return &types.NetworkBinary{ - Path: b.GetPath(), - } -} - -// NetworkBinaryToProto converts an SDK NetworkBinary to a proto NetworkBinary. -func NetworkBinaryToProto(b *types.NetworkBinary) *sbv1.NetworkBinary { - if b == nil { - return nil - } - return &sbv1.NetworkBinary{ - Path: b.Path, - } -} - -// --- ProfileCredential --- - -// ProfileCredentialFromProto converts a proto ProviderProfileCredential to an SDK ProfileCredential. -// Secret is derived from whether the proto has a Refresh configuration. -func ProfileCredentialFromProto(c *pb.ProviderProfileCredential) *types.ProfileCredential { - if c == nil { - return nil - } - return &types.ProfileCredential{ - Name: c.GetName(), - Description: c.GetDescription(), - EnvVars: CopyStringSlice(c.GetEnvVars()), - Required: c.GetRequired(), - StablePlaceholder: c.GetStablePlaceholder(), - Secret: c.GetRefresh() != nil, - Refresh: profileCredentialRefreshFromProto(c.GetRefresh()), - AuthStyle: c.GetAuthStyle(), - HeaderName: c.GetHeaderName(), - QueryParam: c.GetQueryParam(), - PathTemplate: c.GetPathTemplate(), - TokenGrant: tokenGrantFromProto(c.GetTokenGrant()), - } -} - -// ProfileCredentialToProto converts an SDK ProfileCredential to a proto ProviderProfileCredential. -func ProfileCredentialToProto(c *types.ProfileCredential) *pb.ProviderProfileCredential { - if c == nil { - return nil - } - return &pb.ProviderProfileCredential{ - Name: c.Name, - Description: c.Description, - EnvVars: CopyStringSlice(c.EnvVars), - Required: c.Required, - StablePlaceholder: c.StablePlaceholder, - AuthStyle: c.AuthStyle, - HeaderName: c.HeaderName, - QueryParam: c.QueryParam, - PathTemplate: c.PathTemplate, - Refresh: profileCredentialRefreshToProto(c.Refresh), - TokenGrant: tokenGrantToProto(c.TokenGrant), - } -} - -func profileDurationFromProto(value *durationpb.Duration) *types.ProfileDuration { - if value == nil { - return nil - } - return &types.ProfileDuration{Seconds: value.Seconds, Nanos: value.Nanos} -} - -func profileDurationToProto(value *types.ProfileDuration) *durationpb.Duration { - if value == nil { - return nil - } - return &durationpb.Duration{Seconds: value.Seconds, Nanos: value.Nanos} -} - -func profileCredentialRefreshFromProto(r *pb.ProviderCredentialRefresh) *types.ProfileCredentialRefresh { - if r == nil { - return nil - } - result := &types.ProfileCredentialRefresh{ - Strategy: RefreshStrategyFromProto(r.GetStrategy()), TokenURL: r.GetTokenUrl(), - Scopes: CopyStringSlice(r.GetScopes()), RefreshBefore: profileDurationFromProto(r.RefreshBefore), - MaxLifetime: profileDurationFromProto(r.MaxLifetime), - } - for _, material := range r.GetMaterial() { - result.Material = append(result.Material, types.ProfileCredentialRefreshMaterial{Name: material.GetName(), Description: material.GetDescription(), Required: material.GetRequired(), Secret: material.GetSecret()}) - } - for _, output := range r.GetAdditionalOutputs() { - result.AdditionalOutputs = append(result.AdditionalOutputs, types.ProfileCredentialRefreshOutput{Output: output.GetOutput(), Credential: output.GetCredential()}) - } - return result -} - -func profileCredentialRefreshToProto(r *types.ProfileCredentialRefresh) *pb.ProviderCredentialRefresh { - if r == nil { - return nil - } - result := &pb.ProviderCredentialRefresh{ - Strategy: RefreshStrategyToProto(r.Strategy), TokenUrl: r.TokenURL, - Scopes: CopyStringSlice(r.Scopes), RefreshBefore: profileDurationToProto(r.RefreshBefore), - MaxLifetime: profileDurationToProto(r.MaxLifetime), - } - for _, material := range r.Material { - result.Material = append(result.Material, &pb.ProviderCredentialRefreshMaterial{Name: material.Name, Description: material.Description, Required: material.Required, Secret: material.Secret}) - } - for _, output := range r.AdditionalOutputs { - result.AdditionalOutputs = append(result.AdditionalOutputs, &pb.ProviderCredentialRefreshOutput{Output: output.Output, Credential: output.Credential}) - } - return result -} - -func tokenGrantFromProto(tg *pb.ProviderCredentialTokenGrant) *types.CredentialTokenGrant { - if tg == nil { - return nil - } - result := &types.CredentialTokenGrant{ - TokenEndpoint: tg.GetTokenEndpoint(), - Audience: tg.GetAudience(), - JWTSVIDAudience: tg.GetJwtSvidAudience(), - Scopes: CopyStringSlice(tg.GetScopes()), - CacheTTL: profileDurationFromProto(tg.CacheTtl), - ClientAssertionType: tg.GetClientAssertionType(), - GrantType: CredentialTokenGrantTypeFromProto(tg.GetGrantType()), - SubjectToken: subjectTokenFromProto(tg.GetSubjectToken()), - RequestedTokenType: tg.GetRequestedTokenType(), - } - if overrides := tg.GetAudienceOverrides(); len(overrides) > 0 { - result.AudienceOverrides = make([]types.TokenGrantAudienceOverride, len(overrides)) - for i, o := range overrides { - result.AudienceOverrides[i] = audienceOverrideFromProto(o) - } - } - return result -} - -func tokenGrantToProto(tg *types.CredentialTokenGrant) *pb.ProviderCredentialTokenGrant { - if tg == nil { - return nil - } - result := &pb.ProviderCredentialTokenGrant{ - TokenEndpoint: tg.TokenEndpoint, - Audience: tg.Audience, - JwtSvidAudience: tg.JWTSVIDAudience, - Scopes: CopyStringSlice(tg.Scopes), - CacheTtl: profileDurationToProto(tg.CacheTTL), - ClientAssertionType: tg.ClientAssertionType, - GrantType: CredentialTokenGrantTypeToProto(tg.GrantType), - SubjectToken: subjectTokenToProto(tg.SubjectToken), - RequestedTokenType: tg.RequestedTokenType, - } - if len(tg.AudienceOverrides) > 0 { - result.AudienceOverrides = make([]*pb.ProviderCredentialTokenGrantAudienceOverride, len(tg.AudienceOverrides)) - for i := range tg.AudienceOverrides { - result.AudienceOverrides[i] = audienceOverrideToProto(&tg.AudienceOverrides[i]) - } - } - return result -} - -func subjectTokenFromProto(st *pb.ProviderCredentialTokenGrantSubjectToken) *types.TokenGrantSubjectToken { - if st == nil { - return nil - } - return &types.TokenGrantSubjectToken{ - Source: st.GetSource(), - Credential: st.GetCredential(), - SubjectTokenType: st.GetSubjectTokenType(), - } -} - -func subjectTokenToProto(st *types.TokenGrantSubjectToken) *pb.ProviderCredentialTokenGrantSubjectToken { - if st == nil { - return nil - } - return &pb.ProviderCredentialTokenGrantSubjectToken{ - Source: st.Source, - Credential: st.Credential, - SubjectTokenType: st.SubjectTokenType, - } -} - -func audienceOverrideFromProto(o *pb.ProviderCredentialTokenGrantAudienceOverride) types.TokenGrantAudienceOverride { - if o == nil { - return types.TokenGrantAudienceOverride{} - } - return types.TokenGrantAudienceOverride{ - Host: o.GetHost(), - Port: o.GetPort(), - Path: o.GetPath(), - Audience: o.GetAudience(), - Scopes: CopyStringSlice(o.GetScopes()), - } -} - -func audienceOverrideToProto(o *types.TokenGrantAudienceOverride) *pb.ProviderCredentialTokenGrantAudienceOverride { - if o == nil { - return nil - } - return &pb.ProviderCredentialTokenGrantAudienceOverride{ - Host: o.Host, - Port: o.Port, - Path: o.Path, - Audience: o.Audience, - Scopes: CopyStringSlice(o.Scopes), - } -} - -// --- ProfileDiagnostic --- - -// ProfileDiagnosticFromProto converts a proto ProviderProfileDiagnostic to an SDK ProfileDiagnostic. -func ProfileDiagnosticFromProto(d *pb.ProviderProfileDiagnostic) *types.ProfileDiagnostic { - if d == nil { - return nil - } - return &types.ProfileDiagnostic{ - Source: d.GetSource(), - ProfileID: d.GetProfileId(), - Field: d.GetField(), - Message: d.GetMessage(), - Severity: d.GetSeverity(), - } -} - -// --- ProviderProfile --- - -// ProviderProfileFromProto converts a proto ProviderProfile to an SDK ProviderProfile. -func ProviderProfileFromProto(p *pb.ProviderProfile) *types.ProviderProfile { - if p == nil { - return nil - } - - result := &types.ProviderProfile{ - ID: p.GetId(), - DisplayName: p.GetDisplayName(), - Description: p.GetDescription(), - Category: ProfileCategoryFromProto(p.GetCategory()), - InferenceCapable: p.GetInferenceCapable(), - ResourceVersion: p.GetResourceVersion(), - Annotations: CopyStringMap(p.GetAnnotations()), - Source: p.GetSource(), - Scope: p.GetScope(), - } - - // Credentials - if creds := p.GetCredentials(); len(creds) > 0 { - result.Credentials = make([]types.ProfileCredential, len(creds)) - for i, c := range creds { - if converted := ProfileCredentialFromProto(c); converted != nil { - result.Credentials[i] = *converted - } - } - } - - // Files - if files := p.GetFiles(); len(files) > 0 { - result.Files = make([]types.ProfileFile, len(files)) - for i, file := range files { - if file != nil { - result.Files[i] = types.ProfileFile{ - Path: file.GetPath(), Content: file.GetContent(), EnvVar: file.GetEnvVar(), - } - } - } - } - - // Endpoints - if eps := p.GetEndpoints(); len(eps) > 0 { - result.Endpoints = make([]types.NetworkEndpoint, len(eps)) - for i, ep := range eps { - if converted := NetworkEndpointFromProto(ep); converted != nil { - result.Endpoints[i] = *converted - } - } - } - - // Binaries - if bins := p.GetBinaries(); len(bins) > 0 { - result.Binaries = make([]types.NetworkBinary, len(bins)) - for i, b := range bins { - if converted := NetworkBinaryFromProto(b); converted != nil { - result.Binaries[i] = *converted - } - } - } - - // Discovery - if d := p.GetDiscovery(); d != nil { - result.Discovery = types.ProfileDiscovery{ - Credentials: CopyStringSlice(d.GetCredentials()), - } - } - - return result -} - -// ProviderProfileToProto converts an SDK ProviderProfile to a proto ProviderProfile. -func ProviderProfileToProto(p *types.ProviderProfile) *pb.ProviderProfile { - if p == nil { - return nil - } - - result := &pb.ProviderProfile{ - Id: p.ID, - DisplayName: p.DisplayName, - Description: p.Description, - Category: ProfileCategoryToProto(p.Category), - InferenceCapable: p.InferenceCapable, - ResourceVersion: p.ResourceVersion, - Annotations: CopyStringMap(p.Annotations), - Source: p.Source, - Scope: p.Scope, - } - - // Credentials - if len(p.Credentials) > 0 { - result.Credentials = make([]*pb.ProviderProfileCredential, len(p.Credentials)) - for i := range p.Credentials { - result.Credentials[i] = ProfileCredentialToProto(&p.Credentials[i]) - } - } - - // Files - if len(p.Files) > 0 { - result.Files = make([]*pb.ProviderProfileFile, len(p.Files)) - for i, file := range p.Files { - result.Files[i] = &pb.ProviderProfileFile{ - Path: file.Path, Content: file.Content, EnvVar: file.EnvVar, - } - } - } - - // Endpoints - if len(p.Endpoints) > 0 { - result.Endpoints = make([]*sbv1.NetworkEndpoint, len(p.Endpoints)) - for i := range p.Endpoints { - result.Endpoints[i] = NetworkEndpointToProto(&p.Endpoints[i]) - } - } - - // Binaries - if len(p.Binaries) > 0 { - result.Binaries = make([]*sbv1.NetworkBinary, len(p.Binaries)) - for i := range p.Binaries { - result.Binaries[i] = NetworkBinaryToProto(&p.Binaries[i]) - } - } - - // Discovery - if len(p.Discovery.Credentials) > 0 { - result.Discovery = &pb.ProviderProfileDiscovery{ - Credentials: CopyStringSlice(p.Discovery.Credentials), - } - } - - return result -} - -// --- ProfileImportItem --- - -// ProfileImportItemToProto converts an SDK ProfileImportItem to a proto ProviderProfileImportItem. -func ProfileImportItemToProto(item *types.ProfileImportItem) *pb.ProviderProfileImportItem { - if item == nil { - return nil - } - return &pb.ProviderProfileImportItem{ - Profile: ProviderProfileToProto(&item.Profile), - Source: item.Source, - } -} - -// ProfileImportItemFromProto converts a proto ProviderProfileImportItem to an SDK ProfileImportItem. -func ProfileImportItemFromProto(item *pb.ProviderProfileImportItem) *types.ProfileImportItem { - if item == nil { - return nil - } - - result := &types.ProfileImportItem{ - Source: item.GetSource(), - } - - if p := ProviderProfileFromProto(item.GetProfile()); p != nil { - result.Profile = *p - } - - return result -} diff --git a/sdk/go/openshell/v1/internal/converter/profile_test.go b/sdk/go/openshell/v1/internal/converter/profile_test.go deleted file mode 100644 index 28f58533ba..0000000000 --- a/sdk/go/openshell/v1/internal/converter/profile_test.go +++ /dev/null @@ -1,753 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - sbv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/types/known/durationpb" -) - -// --- ProfileCategory --- - -func TestProfileCategoryFromProto(t *testing.T) { - tests := []struct { - proto pb.ProviderProfileCategory - want v1.ProfileCategory - }{ - {pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_OTHER, v1.ProfileCategoryOther}, - {pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE, v1.ProfileCategoryInference}, - {pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_AGENT, v1.ProfileCategoryAgent}, - {pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL, v1.ProfileCategorySourceControl}, - {pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_MESSAGING, v1.ProfileCategoryMessaging}, - {pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_DATA, v1.ProfileCategoryData}, - {pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_KNOWLEDGE, v1.ProfileCategoryKnowledge}, - {pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_UNSPECIFIED, v1.ProfileCategory("")}, - } - for _, tt := range tests { - t.Run(tt.proto.String(), func(t *testing.T) { - assert.Equal(t, tt.want, ProfileCategoryFromProto(tt.proto)) - }) - } -} - -func TestProfileCategoryToProto(t *testing.T) { - tests := []struct { - sdk v1.ProfileCategory - want pb.ProviderProfileCategory - }{ - {v1.ProfileCategoryOther, pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_OTHER}, - {v1.ProfileCategoryInference, pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE}, - {v1.ProfileCategoryAgent, pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_AGENT}, - {v1.ProfileCategorySourceControl, pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL}, - {v1.ProfileCategoryMessaging, pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_MESSAGING}, - {v1.ProfileCategoryData, pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_DATA}, - {v1.ProfileCategoryKnowledge, pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_KNOWLEDGE}, - {v1.ProfileCategory(""), pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_UNSPECIFIED}, - {v1.ProfileCategory("Unknown"), pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_UNSPECIFIED}, - } - for _, tt := range tests { - t.Run(string(tt.sdk), func(t *testing.T) { - assert.Equal(t, tt.want, ProfileCategoryToProto(tt.sdk)) - }) - } -} - -// --- NetworkEndpoint --- - -func TestNetworkEndpointFromProto(t *testing.T) { - proto := &sbv1.NetworkEndpoint{ - Host: "api.example.com", - Port: 443, - Protocol: "rest", - } - - ep := NetworkEndpointFromProto(proto) - - require.NotNil(t, ep) - assert.Equal(t, "api.example.com", ep.Host) - assert.Equal(t, uint32(443), ep.Port) - assert.Equal(t, "rest", ep.Protocol) -} - -func TestNetworkEndpointFromProto_Nil(t *testing.T) { - ep := NetworkEndpointFromProto(nil) - assert.Nil(t, ep) -} - -func TestNetworkEndpointToProto(t *testing.T) { - ep := &v1.NetworkEndpoint{ - Host: "api.example.com", - Port: 443, - Protocol: "rest", - } - - proto := NetworkEndpointToProto(ep) - - require.NotNil(t, proto) - assert.Equal(t, "api.example.com", proto.Host) - assert.Equal(t, uint32(443), proto.Port) - assert.Equal(t, "rest", proto.Protocol) -} - -func TestNetworkEndpointToProto_Nil(t *testing.T) { - proto := NetworkEndpointToProto(nil) - assert.Nil(t, proto) -} - -// --- NetworkBinary --- - -func TestNetworkBinaryFromProto(t *testing.T) { - proto := &sbv1.NetworkBinary{ - Path: "/usr/local/bin/tool", - } - - bin := NetworkBinaryFromProto(proto) - - require.NotNil(t, bin) - assert.Equal(t, "/usr/local/bin/tool", bin.Path) -} - -func TestNetworkBinaryFromProto_Nil(t *testing.T) { - bin := NetworkBinaryFromProto(nil) - assert.Nil(t, bin) -} - -func TestNetworkBinaryToProto(t *testing.T) { - bin := &v1.NetworkBinary{ - Path: "/usr/local/bin/tool", - } - - proto := NetworkBinaryToProto(bin) - - require.NotNil(t, proto) - assert.Equal(t, "/usr/local/bin/tool", proto.Path) -} - -func TestNetworkBinaryToProto_Nil(t *testing.T) { - proto := NetworkBinaryToProto(nil) - assert.Nil(t, proto) -} - -// --- ProfileCredential --- - -func TestProfileCredentialFromProto(t *testing.T) { - proto := &pb.ProviderProfileCredential{ - Name: "API_KEY", - Description: "API key for auth", - EnvVars: []string{"ANTHROPIC_API_KEY", "API_KEY"}, - Required: true, - StablePlaceholder: true, - AuthStyle: "header", - HeaderName: "X-API-Key", - QueryParam: "api_key", - PathTemplate: "/v1/{credential}/chat", - Refresh: &pb.ProviderCredentialRefresh{ - Strategy: pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN, - }, - TokenGrant: &pb.ProviderCredentialTokenGrant{ - TokenEndpoint: "https://auth.example.com/token", - Audience: "https://api.example.com", - JwtSvidAudience: "spiffe://example.com", - Scopes: []string{"read", "write"}, - CacheTtl: DurationFromSeconds(300), - ClientAssertionType: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", - GrantType: pb.ProviderCredentialTokenGrantType_PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_TOKEN_EXCHANGE, - SubjectToken: &pb.ProviderCredentialTokenGrantSubjectToken{ - Source: "provider_credential", - Credential: "UPSTREAM_TOKEN", - SubjectTokenType: "urn:ietf:params:oauth:token-type:access_token", - }, - RequestedTokenType: "urn:ietf:params:oauth:token-type:refresh_token", - AudienceOverrides: []*pb.ProviderCredentialTokenGrantAudienceOverride{ - {Host: "special.example.com", Port: 8443, Path: "/api", Audience: "https://special.example.com", Scopes: []string{"admin"}}, - }, - }, - } - - cred := ProfileCredentialFromProto(proto) - - require.NotNil(t, cred) - assert.Equal(t, "API_KEY", cred.Name) - assert.Equal(t, "API key for auth", cred.Description) - assert.Equal(t, []string{"ANTHROPIC_API_KEY", "API_KEY"}, cred.EnvVars) - assert.True(t, cred.Required) - assert.True(t, cred.StablePlaceholder) - assert.True(t, cred.Secret, "credential with refresh config is secret") - assert.Equal(t, "header", cred.AuthStyle) - assert.Equal(t, "X-API-Key", cred.HeaderName) - assert.Equal(t, "api_key", cred.QueryParam) - assert.Equal(t, "/v1/{credential}/chat", cred.PathTemplate) - - require.NotNil(t, cred.TokenGrant) - assert.Equal(t, "https://auth.example.com/token", cred.TokenGrant.TokenEndpoint) - assert.Equal(t, "https://api.example.com", cred.TokenGrant.Audience) - assert.Equal(t, "spiffe://example.com", cred.TokenGrant.JWTSVIDAudience) - assert.Equal(t, []string{"read", "write"}, cred.TokenGrant.Scopes) - assert.Equal(t, &v1.ProfileDuration{Seconds: 300}, cred.TokenGrant.CacheTTL) - assert.Equal(t, "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", cred.TokenGrant.ClientAssertionType) - assert.Equal(t, v1.CredentialTokenGrantTypeTokenExchange, cred.TokenGrant.GrantType) - require.NotNil(t, cred.TokenGrant.SubjectToken) - assert.Equal(t, "provider_credential", cred.TokenGrant.SubjectToken.Source) - assert.Equal(t, "UPSTREAM_TOKEN", cred.TokenGrant.SubjectToken.Credential) - assert.Equal(t, "urn:ietf:params:oauth:token-type:access_token", cred.TokenGrant.SubjectToken.SubjectTokenType) - assert.Equal(t, "urn:ietf:params:oauth:token-type:refresh_token", cred.TokenGrant.RequestedTokenType) - require.Len(t, cred.TokenGrant.AudienceOverrides, 1) - assert.Equal(t, "special.example.com", cred.TokenGrant.AudienceOverrides[0].Host) - assert.Equal(t, uint32(8443), cred.TokenGrant.AudienceOverrides[0].Port) - assert.Equal(t, "/api", cred.TokenGrant.AudienceOverrides[0].Path) - assert.Equal(t, "https://special.example.com", cred.TokenGrant.AudienceOverrides[0].Audience) - assert.Equal(t, []string{"admin"}, cred.TokenGrant.AudienceOverrides[0].Scopes) -} - -func TestProfileCredentialDurationRoundTripPreservesPresencePrecisionAndValidationSignal(t *testing.T) { - original := &pb.ProviderProfileCredential{ - Refresh: &pb.ProviderCredentialRefresh{ - RefreshBefore: &durationpb.Duration{Nanos: 500_000_000}, - MaxLifetime: &durationpb.Duration{Seconds: 1, Nanos: -1}, - }, - TokenGrant: &pb.ProviderCredentialTokenGrant{ - CacheTtl: &durationpb.Duration{Seconds: 1, Nanos: -1}, - }, - } - credential := ProfileCredentialFromProto(original) - - assert.Equal(t, &v1.ProfileDuration{Nanos: 500_000_000}, credential.Refresh.RefreshBefore) - assert.Equal(t, &v1.ProfileDuration{Seconds: 1, Nanos: -1}, credential.Refresh.MaxLifetime) - assert.Equal(t, &v1.ProfileDuration{Seconds: 1, Nanos: -1}, credential.TokenGrant.CacheTTL) - - roundTripped := ProfileCredentialToProto(credential) - assert.Equal(t, original.Refresh.RefreshBefore.Seconds, roundTripped.Refresh.RefreshBefore.Seconds) - assert.Equal(t, original.Refresh.RefreshBefore.Nanos, roundTripped.Refresh.RefreshBefore.Nanos) - assert.Equal(t, original.Refresh.MaxLifetime.Seconds, roundTripped.Refresh.MaxLifetime.Seconds) - assert.Equal(t, original.Refresh.MaxLifetime.Nanos, roundTripped.Refresh.MaxLifetime.Nanos) - assert.Equal(t, original.TokenGrant.CacheTtl.Seconds, roundTripped.TokenGrant.CacheTtl.Seconds) - assert.Equal(t, original.TokenGrant.CacheTtl.Nanos, roundTripped.TokenGrant.CacheTtl.Nanos) -} - -func TestProfileCredentialDurationRoundTripDistinguishesAbsentAndZero(t *testing.T) { - original := &pb.ProviderProfileCredential{ - Refresh: &pb.ProviderCredentialRefresh{ - RefreshBefore: &durationpb.Duration{}, - MaxLifetime: nil, - }, - } - - roundTripped := ProfileCredentialToProto(ProfileCredentialFromProto(original)) - require.NotNil(t, roundTripped.Refresh.RefreshBefore) - assert.Equal(t, int64(0), roundTripped.Refresh.RefreshBefore.Seconds) - assert.Nil(t, roundTripped.Refresh.MaxLifetime) -} - -func TestProfileCredentialDurationExactValuesAreAuthoritative(t *testing.T) { - credential := &v1.ProfileCredential{ - Refresh: &v1.ProfileCredentialRefresh{ - RefreshBefore: &v1.ProfileDuration{Seconds: 60}, - MaxLifetime: &v1.ProfileDuration{Seconds: 3600, Nanos: 500_000_000}, - }, - TokenGrant: &v1.CredentialTokenGrant{ - CacheTTL: &v1.ProfileDuration{Nanos: 500_000_000}, - }, - } - - converted := ProfileCredentialToProto(credential) - assert.Equal(t, &durationpb.Duration{Seconds: 60}, converted.Refresh.RefreshBefore) - assert.Equal(t, &durationpb.Duration{Seconds: 3600, Nanos: 500_000_000}, converted.Refresh.MaxLifetime) - assert.Equal(t, &durationpb.Duration{Nanos: 500_000_000}, converted.TokenGrant.CacheTtl) -} - -func TestProfileCredentialDurationPreservesExactMutationAfterRead(t *testing.T) { - credential := ProfileCredentialFromProto(&pb.ProviderProfileCredential{ - TokenGrant: &pb.ProviderCredentialTokenGrant{ - CacheTtl: &durationpb.Duration{Seconds: 30, Nanos: 500_000_000}, - }, - }) - credential.TokenGrant.CacheTTL.Seconds = 60 - - roundTripped := ProfileCredentialToProto(credential) - assert.Equal(t, int64(60), roundTripped.TokenGrant.CacheTtl.Seconds) - assert.Equal(t, int32(500_000_000), roundTripped.TokenGrant.CacheTtl.Nanos) -} - -func TestProfileCredentialFromProto_DeepCopy(t *testing.T) { - proto := &pb.ProviderProfileCredential{ - Name: "KEY", - EnvVars: []string{"ENV_A"}, - TokenGrant: &pb.ProviderCredentialTokenGrant{ - Scopes: []string{"read"}, - AudienceOverrides: []*pb.ProviderCredentialTokenGrantAudienceOverride{ - {Scopes: []string{"admin"}}, - }, - }, - } - - cred := ProfileCredentialFromProto(proto) - - proto.EnvVars[0] = "MUTATED" - assert.Equal(t, "ENV_A", cred.EnvVars[0], "env_vars must be deep copied") - - proto.TokenGrant.Scopes[0] = "MUTATED" - assert.Equal(t, "read", cred.TokenGrant.Scopes[0], "token grant scopes must be deep copied") - - proto.TokenGrant.AudienceOverrides[0].Scopes[0] = "MUTATED" - assert.Equal(t, "admin", cred.TokenGrant.AudienceOverrides[0].Scopes[0], "audience override scopes must be deep copied") -} - -func TestProfileCredentialFromProto_NotSecret(t *testing.T) { - proto := &pb.ProviderProfileCredential{ - Name: "ENDPOINT_URL", - Required: false, - } - - cred := ProfileCredentialFromProto(proto) - - require.NotNil(t, cred) - assert.Equal(t, "ENDPOINT_URL", cred.Name) - assert.False(t, cred.Required) - assert.False(t, cred.Secret, "credential without refresh config is not secret") - assert.Nil(t, cred.TokenGrant) -} - -func TestProfileCredentialFromProto_Nil(t *testing.T) { - cred := ProfileCredentialFromProto(nil) - assert.Nil(t, cred) -} - -func TestProfileCredentialToProto(t *testing.T) { - cred := &v1.ProfileCredential{ - Name: "API_KEY", - Description: "API key", - EnvVars: []string{"ANTHROPIC_API_KEY"}, - Required: true, - StablePlaceholder: true, - Secret: true, - Refresh: &v1.ProfileCredentialRefresh{ - Strategy: v1.RefreshStrategyOAuth2RefreshToken, - TokenURL: "https://auth.example.com/token", - Scopes: []string{"offline_access"}, - RefreshBefore: &v1.ProfileDuration{Seconds: 60}, - MaxLifetime: &v1.ProfileDuration{Seconds: 3600}, - Material: []v1.ProfileCredentialRefreshMaterial{{Name: "refresh_token", Required: true, Secret: true}}, - AdditionalOutputs: []v1.ProfileCredentialRefreshOutput{{Output: "session_token", Credential: "SESSION_TOKEN"}}, - }, - AuthStyle: "header", - HeaderName: "X-API-Key", - QueryParam: "api_key", - PathTemplate: "/v1/{credential}/chat", - TokenGrant: &v1.CredentialTokenGrant{ - TokenEndpoint: "https://auth.example.com/token", - Audience: "https://api.example.com", - JWTSVIDAudience: "spiffe://example.com", - Scopes: []string{"read"}, - CacheTTL: &v1.ProfileDuration{Seconds: 300}, - ClientAssertionType: "urn:custom", - GrantType: v1.CredentialTokenGrantTypeTokenExchange, - SubjectToken: &v1.TokenGrantSubjectToken{ - Source: "provider_credential", - Credential: "UPSTREAM_TOKEN", - SubjectTokenType: "urn:ietf:params:oauth:token-type:access_token", - }, - RequestedTokenType: "urn:ietf:params:oauth:token-type:refresh_token", - AudienceOverrides: []v1.TokenGrantAudienceOverride{ - {Host: "h", Port: 443, Path: "/p", Audience: "aud", Scopes: []string{"s"}}, - }, - }, - } - - proto := ProfileCredentialToProto(cred) - - require.NotNil(t, proto) - assert.Equal(t, "API_KEY", proto.Name) - assert.Equal(t, "API key", proto.Description) - assert.Equal(t, []string{"ANTHROPIC_API_KEY"}, proto.EnvVars) - assert.True(t, proto.Required) - assert.True(t, proto.StablePlaceholder) - assert.Equal(t, "header", proto.AuthStyle) - assert.Equal(t, "X-API-Key", proto.HeaderName) - assert.Equal(t, "api_key", proto.QueryParam) - assert.Equal(t, "/v1/{credential}/chat", proto.PathTemplate) - require.NotNil(t, proto.Refresh) - assert.Equal(t, pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN, proto.Refresh.Strategy) - assert.Equal(t, "https://auth.example.com/token", proto.Refresh.TokenUrl) - assert.Equal(t, []string{"offline_access"}, proto.Refresh.Scopes) - assert.Equal(t, &durationpb.Duration{Seconds: 60}, proto.Refresh.RefreshBefore) - assert.Equal(t, &durationpb.Duration{Seconds: 3600}, proto.Refresh.MaxLifetime) - require.Len(t, proto.Refresh.Material, 1) - require.Len(t, proto.Refresh.AdditionalOutputs, 1) - - require.NotNil(t, proto.TokenGrant) - assert.Equal(t, "https://auth.example.com/token", proto.TokenGrant.TokenEndpoint) - assert.Equal(t, "https://api.example.com", proto.TokenGrant.Audience) - assert.Equal(t, "spiffe://example.com", proto.TokenGrant.JwtSvidAudience) - assert.Equal(t, []string{"read"}, proto.TokenGrant.Scopes) - assert.Equal(t, int64(300), proto.TokenGrant.CacheTtl.GetSeconds()) - assert.Equal(t, "urn:custom", proto.TokenGrant.ClientAssertionType) - assert.Equal(t, pb.ProviderCredentialTokenGrantType_PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_TOKEN_EXCHANGE, proto.TokenGrant.GrantType) - require.NotNil(t, proto.TokenGrant.SubjectToken) - assert.Equal(t, "provider_credential", proto.TokenGrant.SubjectToken.Source) - assert.Equal(t, "UPSTREAM_TOKEN", proto.TokenGrant.SubjectToken.Credential) - assert.Equal(t, "urn:ietf:params:oauth:token-type:access_token", proto.TokenGrant.SubjectToken.SubjectTokenType) - assert.Equal(t, "urn:ietf:params:oauth:token-type:refresh_token", proto.TokenGrant.RequestedTokenType) - require.Len(t, proto.TokenGrant.AudienceOverrides, 1) - assert.Equal(t, "h", proto.TokenGrant.AudienceOverrides[0].Host) -} - -func TestProfileCredentialToProto_PreservesInvalidDurationsForValidation(t *testing.T) { - proto := ProfileCredentialToProto(&v1.ProfileCredential{ - Refresh: &v1.ProfileCredentialRefresh{ - RefreshBefore: &v1.ProfileDuration{Seconds: -1}, - MaxLifetime: &v1.ProfileDuration{Seconds: 1, Nanos: -1}, - }, - TokenGrant: &v1.CredentialTokenGrant{ - CacheTTL: &v1.ProfileDuration{Nanos: 1_000_000_000}, - }, - }) - - assert.Equal(t, &durationpb.Duration{Seconds: -1}, proto.Refresh.RefreshBefore) - assert.Equal(t, &durationpb.Duration{Seconds: 1, Nanos: -1}, proto.Refresh.MaxLifetime) - assert.Equal(t, &durationpb.Duration{Nanos: 1_000_000_000}, proto.TokenGrant.CacheTtl) -} - -func TestProfileCredentialToProto_Nil(t *testing.T) { - proto := ProfileCredentialToProto(nil) - assert.Nil(t, proto) -} - -func TestProfileCredentialToProto_DeepCopy(t *testing.T) { - cred := &v1.ProfileCredential{ - Name: "KEY", - EnvVars: []string{"ENV_A"}, - TokenGrant: &v1.CredentialTokenGrant{ - Scopes: []string{"read"}, - }, - } - - proto := ProfileCredentialToProto(cred) - - cred.EnvVars[0] = "MUTATED" - assert.Equal(t, "ENV_A", proto.EnvVars[0], "env_vars must be deep copied") - - cred.TokenGrant.Scopes[0] = "MUTATED" - assert.Equal(t, "read", proto.TokenGrant.Scopes[0], "token grant scopes must be deep copied") -} - -// --- ProfileDiagnostic --- - -func TestProfileDiagnosticFromProto(t *testing.T) { - proto := &pb.ProviderProfileDiagnostic{ - Source: "import", - ProfileId: "prof-1", - Field: "credentials", - Message: "missing required field", - Severity: "error", - } - - diag := ProfileDiagnosticFromProto(proto) - - require.NotNil(t, diag) - assert.Equal(t, "import", diag.Source) - assert.Equal(t, "prof-1", diag.ProfileID) - assert.Equal(t, "credentials", diag.Field) - assert.Equal(t, "missing required field", diag.Message) - assert.Equal(t, "error", diag.Severity) -} - -func TestProfileDiagnosticFromProto_Nil(t *testing.T) { - diag := ProfileDiagnosticFromProto(nil) - assert.Nil(t, diag) -} - -// --- ProviderProfile --- - -func TestProviderProfileFromProto(t *testing.T) { - proto := &pb.ProviderProfile{ - Id: "prof-1", - DisplayName: "Claude Provider", - Description: "Anthropic Claude", - Category: pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE, - Credentials: []*pb.ProviderProfileCredential{ - {Name: "API_KEY", Description: "key", Required: true}, - }, - Files: []*pb.ProviderProfileFile{ - {Path: "client.toml", Content: "project = \"{{config.project}}\"", EnvVar: "CLIENT_CONFIG"}, - }, - Endpoints: []*sbv1.NetworkEndpoint{ - {Host: "api.anthropic.com", Port: 443, Protocol: "rest"}, - }, - Binaries: []*sbv1.NetworkBinary{ - {Path: "/usr/bin/claude"}, - }, - InferenceCapable: true, - Discovery: &pb.ProviderProfileDiscovery{ - Credentials: []string{"API_KEY"}, - }, - ResourceVersion: 7, - Annotations: map[string]string{"env": "prod", "team": "ai"}, - Source: "builtin", - Scope: "platform", - } - - profile := ProviderProfileFromProto(proto) - - require.NotNil(t, profile) - assert.Equal(t, "prof-1", profile.ID) - assert.Equal(t, "Claude Provider", profile.DisplayName) - assert.Equal(t, "Anthropic Claude", profile.Description) - assert.Equal(t, v1.ProfileCategoryInference, profile.Category) - assert.True(t, profile.InferenceCapable) - assert.Equal(t, uint64(7), profile.ResourceVersion) - assert.Equal(t, map[string]string{"env": "prod", "team": "ai"}, profile.Annotations) - assert.Equal(t, "builtin", profile.Source) - assert.Equal(t, "platform", profile.Scope) - - require.Len(t, profile.Credentials, 1) - assert.Equal(t, "API_KEY", profile.Credentials[0].Name) - assert.True(t, profile.Credentials[0].Required) - require.Len(t, profile.Files, 1) - assert.Equal(t, "client.toml", profile.Files[0].Path) - assert.Equal(t, "CLIENT_CONFIG", profile.Files[0].EnvVar) - - require.Len(t, profile.Endpoints, 1) - assert.Equal(t, "api.anthropic.com", profile.Endpoints[0].Host) - assert.Equal(t, uint32(443), profile.Endpoints[0].Port) - - require.Len(t, profile.Binaries, 1) - assert.Equal(t, "/usr/bin/claude", profile.Binaries[0].Path) - - assert.Equal(t, []string{"API_KEY"}, profile.Discovery.Credentials) - - proto.Annotations["env"] = "MUTATED" - assert.Equal(t, "prod", profile.Annotations["env"], "annotations must be deep copied") - proto.Files[0].Content = "changed" - assert.Equal(t, "project = \"{{config.project}}\"", profile.Files[0].Content) -} - -func TestProviderProfileFromProto_NilDiscovery(t *testing.T) { - proto := &pb.ProviderProfile{ - Id: "prof-2", - } - - profile := ProviderProfileFromProto(proto) - - require.NotNil(t, profile) - assert.Nil(t, profile.Discovery.Credentials) -} - -func TestProviderProfileFromProto_Nil(t *testing.T) { - profile := ProviderProfileFromProto(nil) - assert.Nil(t, profile) -} - -func TestProviderProfileToProto(t *testing.T) { - profile := &v1.ProviderProfile{ - ID: "prof-1", - DisplayName: "Claude Provider", - Description: "Anthropic Claude", - Category: v1.ProfileCategoryInference, - Credentials: []v1.ProfileCredential{ - {Name: "API_KEY", Description: "key", Required: true, Secret: true}, - }, - Files: []v1.ProfileFile{ - {Path: "client.toml", Content: "project = \"{{config.project}}\"", EnvVar: "CLIENT_CONFIG"}, - }, - Endpoints: []v1.NetworkEndpoint{ - {Host: "api.anthropic.com", Port: 443, Protocol: "rest"}, - }, - Binaries: []v1.NetworkBinary{ - {Path: "/usr/bin/claude"}, - }, - InferenceCapable: true, - Discovery: v1.ProfileDiscovery{ - Credentials: []string{"API_KEY"}, - }, - ResourceVersion: 7, - Annotations: map[string]string{"env": "prod"}, - Source: "user", - Scope: "workspace", - } - - proto := ProviderProfileToProto(profile) - - require.NotNil(t, proto) - assert.Equal(t, "prof-1", proto.Id) - assert.Equal(t, "Claude Provider", proto.DisplayName) - assert.Equal(t, "Anthropic Claude", proto.Description) - assert.Equal(t, pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE, proto.Category) - assert.True(t, proto.InferenceCapable) - assert.Equal(t, uint64(7), proto.ResourceVersion) - assert.Equal(t, map[string]string{"env": "prod"}, proto.Annotations) - assert.Equal(t, "user", proto.Source) - assert.Equal(t, "workspace", proto.Scope) - - require.Len(t, proto.Credentials, 1) - assert.Equal(t, "API_KEY", proto.Credentials[0].Name) - require.Len(t, proto.Files, 1) - assert.Equal(t, "client.toml", proto.Files[0].Path) - - require.Len(t, proto.Endpoints, 1) - assert.Equal(t, "api.anthropic.com", proto.Endpoints[0].Host) - - require.Len(t, proto.Binaries, 1) - assert.Equal(t, "/usr/bin/claude", proto.Binaries[0].Path) - - require.NotNil(t, proto.Discovery) - assert.Equal(t, []string{"API_KEY"}, proto.Discovery.Credentials) - - profile.Annotations["env"] = "MUTATED" - assert.Equal(t, "prod", proto.Annotations["env"], "annotations must be deep copied") - profile.Files[0].Content = "changed" - assert.Equal(t, "project = \"{{config.project}}\"", proto.Files[0].Content) -} - -func TestProviderProfileToProto_Nil(t *testing.T) { - proto := ProviderProfileToProto(nil) - assert.Nil(t, proto) -} - -// --- ProfileImportItem --- - -func TestProfileImportItemToProto(t *testing.T) { - item := &v1.ProfileImportItem{ - Profile: v1.ProviderProfile{ - ID: "prof-1", - DisplayName: "Test", - Category: v1.ProfileCategoryOther, - }, - Source: "file:///profiles/test.yaml", - } - - proto := ProfileImportItemToProto(item) - - require.NotNil(t, proto) - assert.Equal(t, "file:///profiles/test.yaml", proto.Source) - require.NotNil(t, proto.Profile) - assert.Equal(t, "prof-1", proto.Profile.Id) - assert.Equal(t, "Test", proto.Profile.DisplayName) -} - -func TestProfileImportItemToProto_Nil(t *testing.T) { - proto := ProfileImportItemToProto(nil) - assert.Nil(t, proto) -} - -func TestProfileImportItemFromProto(t *testing.T) { - proto := &pb.ProviderProfileImportItem{ - Profile: &pb.ProviderProfile{ - Id: "prof-1", - DisplayName: "Test", - }, - Source: "file:///profiles/test.yaml", - } - - item := ProfileImportItemFromProto(proto) - - require.NotNil(t, item) - assert.Equal(t, "file:///profiles/test.yaml", item.Source) - assert.Equal(t, "prof-1", item.Profile.ID) -} - -func TestProfileImportItemFromProto_Nil(t *testing.T) { - item := ProfileImportItemFromProto(nil) - assert.Nil(t, item) -} - -// --- ProviderProfile round-trip --- - -func TestProviderProfileRoundTrip(t *testing.T) { - original := &v1.ProviderProfile{ - ID: "prof-rt", - DisplayName: "Round Trip", - Description: "Testing round trip", - Category: v1.ProfileCategoryAgent, - Credentials: []v1.ProfileCredential{ - { - Name: "TOKEN", - Description: "auth token", - EnvVars: []string{"MY_TOKEN"}, - Required: true, - Secret: false, - AuthStyle: "header", - HeaderName: "Authorization", - QueryParam: "token", - PathTemplate: "/api/{credential}", - TokenGrant: &v1.CredentialTokenGrant{ - TokenEndpoint: "https://auth.example.com/token", - Audience: "https://api.example.com", - JWTSVIDAudience: "spiffe://example.com", - Scopes: []string{"read"}, - CacheTTL: &v1.ProfileDuration{Seconds: 600}, - ClientAssertionType: "urn:custom", - AudienceOverrides: []v1.TokenGrantAudienceOverride{ - {Host: "h", Port: 443, Path: "/p", Audience: "aud", Scopes: []string{"s"}}, - }, - }, - }, - }, - Files: []v1.ProfileFile{ - {Path: "client.toml", Content: "project = \"{{config.project}}\"", EnvVar: "CLIENT_CONFIG"}, - }, - Endpoints: []v1.NetworkEndpoint{ - {Host: "agent.example.com", Port: 8080, Protocol: "websocket"}, - }, - Binaries: []v1.NetworkBinary{ - {Path: "/bin/agent"}, - }, - InferenceCapable: false, - Discovery: v1.ProfileDiscovery{ - Credentials: []string{"TOKEN"}, - }, - ResourceVersion: 42, - Annotations: map[string]string{"env": "staging"}, - Source: "interceptor/custom", - Scope: "workspace", - } - - proto := ProviderProfileToProto(original) - back := ProviderProfileFromProto(proto) - - require.NotNil(t, back) - assert.Equal(t, original.ID, back.ID) - assert.Equal(t, original.DisplayName, back.DisplayName) - assert.Equal(t, original.Description, back.Description) - assert.Equal(t, original.Category, back.Category) - assert.Equal(t, original.InferenceCapable, back.InferenceCapable) - assert.Equal(t, original.ResourceVersion, back.ResourceVersion) - assert.Equal(t, original.Annotations, back.Annotations) - assert.Equal(t, original.Source, back.Source) - assert.Equal(t, original.Scope, back.Scope) - assert.Equal(t, original.Files, back.Files) - - require.Len(t, back.Credentials, 1) - c := back.Credentials[0] - assert.Equal(t, original.Credentials[0].Name, c.Name) - assert.Equal(t, original.Credentials[0].Required, c.Required) - assert.Equal(t, original.Credentials[0].EnvVars, c.EnvVars) - assert.Equal(t, original.Credentials[0].AuthStyle, c.AuthStyle) - assert.Equal(t, original.Credentials[0].HeaderName, c.HeaderName) - assert.Equal(t, original.Credentials[0].QueryParam, c.QueryParam) - assert.Equal(t, original.Credentials[0].PathTemplate, c.PathTemplate) - require.NotNil(t, c.TokenGrant) - assert.Equal(t, original.Credentials[0].TokenGrant.TokenEndpoint, c.TokenGrant.TokenEndpoint) - assert.Equal(t, original.Credentials[0].TokenGrant.Audience, c.TokenGrant.Audience) - assert.Equal(t, original.Credentials[0].TokenGrant.JWTSVIDAudience, c.TokenGrant.JWTSVIDAudience) - assert.Equal(t, original.Credentials[0].TokenGrant.Scopes, c.TokenGrant.Scopes) - assert.Equal(t, original.Credentials[0].TokenGrant.CacheTTL, c.TokenGrant.CacheTTL) - assert.Equal(t, original.Credentials[0].TokenGrant.ClientAssertionType, c.TokenGrant.ClientAssertionType) - require.Len(t, c.TokenGrant.AudienceOverrides, 1) - assert.Equal(t, original.Credentials[0].TokenGrant.AudienceOverrides[0], c.TokenGrant.AudienceOverrides[0]) - - require.Len(t, back.Endpoints, 1) - assert.Equal(t, original.Endpoints[0].Host, back.Endpoints[0].Host) - assert.Equal(t, original.Endpoints[0].Port, back.Endpoints[0].Port) - - require.Len(t, back.Binaries, 1) - assert.Equal(t, original.Binaries[0].Path, back.Binaries[0].Path) - - assert.Equal(t, original.Discovery.Credentials, back.Discovery.Credentials) -} diff --git a/sdk/go/openshell/v1/internal/converter/provider.go b/sdk/go/openshell/v1/internal/converter/provider.go deleted file mode 100644 index 119e288be0..0000000000 --- a/sdk/go/openshell/v1/internal/converter/provider.go +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - "google.golang.org/protobuf/types/known/timestamppb" -) - -// ProviderFromProto converts a proto Provider to an SDK Provider. -func ProviderFromProto(p *dm.Provider) *types.Provider { - if p == nil { - return nil - } - - result := &types.Provider{ - Type: p.GetType(), - Spec: types.ProviderSpec{ - Config: CopyStringMap(p.GetConfig()), - ProfileWorkspace: p.GetProfileWorkspace(), - }, - } - - if m := p.GetMetadata(); m != nil { - result.ID = m.GetId() - result.Name = m.GetName() - result.CreatedAt = TimeFromProto(m.GetCreatedTime()) - result.Labels = CopyStringMap(m.GetLabels()) - result.Annotations = CopyStringMap(m.GetAnnotations()) - result.ResourceVersion = m.GetResourceVersion() - result.Workspace = m.GetWorkspace() - result.DeletionTimestamp = TimePtrFromProto(m.GetDeletionTime()) - } - - if expires := p.GetCredentialExpirationTimes(); len(expires) > 0 { - result.Spec.CredentialExpiresAt = make(map[string]time.Time, len(expires)) - for k, timestamp := range expires { - result.Spec.CredentialExpiresAt[k] = TimeFromProto(timestamp) - } - } - - if handles := p.GetCredentialHandles(); len(handles) > 0 { - result.Spec.CredentialHandles = make(map[string]types.CredentialHandle, len(handles)) - for k, h := range handles { - result.Spec.CredentialHandles[k] = types.CredentialHandle{ - Driver: h.GetDriver(), - Handle: h.GetHandle(), - Metadata: CopyStringMap(h.GetMetadata()), - } - } - } - - return result -} - -// ProviderToProto converts an SDK Provider to a proto Provider. -func ProviderToProto(p *types.Provider) *dm.Provider { - if p == nil { - return nil - } - - result := &dm.Provider{ - Metadata: &dm.ObjectMeta{ - Id: p.ID, - Name: p.Name, - CreatedTime: TimestampFromTime(p.CreatedAt), - Labels: CopyStringMap(p.Labels), - Annotations: CopyStringMap(p.Annotations), - ResourceVersion: p.ResourceVersion, - Workspace: p.Workspace, - DeletionTime: TimestampFromTimePtr(p.DeletionTimestamp), - }, - Type: p.Type, - Credentials: CopyStringMap(p.Spec.Credentials), - Config: CopyStringMap(p.Spec.Config), - ProfileWorkspace: p.Spec.ProfileWorkspace, - } - - if len(p.Spec.CredentialExpiresAt) > 0 { - result.CredentialExpirationTimes = make(map[string]*timestamppb.Timestamp, len(p.Spec.CredentialExpiresAt)) - for k, t := range p.Spec.CredentialExpiresAt { - if !t.IsZero() { - result.CredentialExpirationTimes[k] = TimestampFromTime(t) - } - } - } - - if len(p.Spec.CredentialHandles) > 0 { - result.CredentialHandles = make(map[string]*dm.CredentialHandle, len(p.Spec.CredentialHandles)) - for k, h := range p.Spec.CredentialHandles { - result.CredentialHandles[k] = &dm.CredentialHandle{ - Driver: h.Driver, - Handle: h.Handle, - Metadata: CopyStringMap(h.Metadata), - } - } - } - - return result -} diff --git a/sdk/go/openshell/v1/internal/converter/provider_test.go b/sdk/go/openshell/v1/internal/converter/provider_test.go deleted file mode 100644 index 614077a3d9..0000000000 --- a/sdk/go/openshell/v1/internal/converter/provider_test.go +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/types/known/timestamppb" -) - -func TestProviderFromProto_Nil(t *testing.T) { - assert.Nil(t, ProviderFromProto(nil)) -} - -func TestProviderFromProto_Full(t *testing.T) { - proto := &dm.Provider{ - Metadata: &dm.ObjectMeta{ - Id: "prov-1", - Name: "claude-provider", - CreatedTime: TimestampFromMillis(1700000000000), - Labels: map[string]string{"env": "prod"}, - Annotations: map[string]string{"note": "test"}, - ResourceVersion: 42, - Workspace: "default", - }, - Type: "claude", - Credentials: map[string]string{"api_key": "secret"}, - Config: map[string]string{"base_url": "https://api.example.com"}, - ProfileWorkspace: "shared", - CredentialExpirationTimes: map[string]*timestamppb.Timestamp{ - "api_key": TimestampFromMillis(1700003600000), - }, - CredentialHandles: map[string]*dm.CredentialHandle{ - "api_key": { - Driver: "vault", - Handle: "secret/data/claude", - Metadata: map[string]string{"version": "3"}, - }, - }, - } - - result := ProviderFromProto(proto) - - require.NotNil(t, result) - assert.Equal(t, "prov-1", result.ID) - assert.Equal(t, "claude-provider", result.Name) - assert.Equal(t, "claude", result.Type) - assert.Equal(t, uint64(42), result.ResourceVersion) - assert.Equal(t, "default", result.Workspace) - assert.Equal(t, map[string]string{"env": "prod"}, result.Labels) - assert.Equal(t, map[string]string{"note": "test"}, result.Annotations) - assert.Equal(t, map[string]string{"base_url": "https://api.example.com"}, result.Spec.Config) - assert.Equal(t, "shared", result.Spec.ProfileWorkspace) - - require.Len(t, result.Spec.CredentialExpiresAt, 1) - assert.False(t, result.Spec.CredentialExpiresAt["api_key"].IsZero()) - - require.Len(t, result.Spec.CredentialHandles, 1) - h := result.Spec.CredentialHandles["api_key"] - assert.Equal(t, "vault", h.Driver) - assert.Equal(t, "secret/data/claude", h.Handle) - assert.Equal(t, map[string]string{"version": "3"}, h.Metadata) -} - -func TestProviderFromProto_NilMetadata(t *testing.T) { - proto := &dm.Provider{ - Type: "openai", - Config: map[string]string{"key": "val"}, - } - - result := ProviderFromProto(proto) - - require.NotNil(t, result) - assert.Equal(t, "", result.ID) - assert.Equal(t, "", result.Name) - assert.Equal(t, "openai", result.Type) - assert.Equal(t, map[string]string{"key": "val"}, result.Spec.Config) -} - -func TestProviderFromProto_EmptyHandles(t *testing.T) { - proto := &dm.Provider{ - Type: "test", - CredentialHandles: map[string]*dm.CredentialHandle{}, - } - - result := ProviderFromProto(proto) - - require.NotNil(t, result) - assert.Nil(t, result.Spec.CredentialHandles) -} - -func TestProviderToProto_Nil(t *testing.T) { - assert.Nil(t, ProviderToProto(nil)) -} - -func TestProviderToProto_Full(t *testing.T) { - expires := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) - provider := &types.Provider{ - ID: "prov-1", - Name: "test-provider", - Type: "claude", - Labels: map[string]string{"env": "dev"}, - Annotations: map[string]string{"note": "x"}, - ResourceVersion: 7, - Workspace: "ws-1", - Spec: types.ProviderSpec{ - Credentials: map[string]string{"token": "abc"}, - Config: map[string]string{"url": "https://example.com"}, - ProfileWorkspace: "global", - CredentialExpiresAt: map[string]time.Time{"token": expires}, - CredentialHandles: map[string]types.CredentialHandle{ - "token": { - Driver: "k8s-secrets", - Handle: "ns/secret-name", - Metadata: map[string]string{"k": "v"}, - }, - }, - }, - } - - result := ProviderToProto(provider) - - require.NotNil(t, result) - assert.Equal(t, "prov-1", result.Metadata.Id) - assert.Equal(t, "test-provider", result.Metadata.Name) - assert.Equal(t, "claude", result.Type) - assert.Equal(t, "global", result.ProfileWorkspace) - assert.Equal(t, map[string]string{"token": "abc"}, result.Credentials) - assert.Equal(t, map[string]string{"url": "https://example.com"}, result.Config) - - require.Len(t, result.CredentialExpirationTimes, 1) - assert.Greater(t, MillisFromProto(result.CredentialExpirationTimes["token"]), int64(0)) - - require.Len(t, result.CredentialHandles, 1) - h := result.CredentialHandles["token"] - assert.Equal(t, "k8s-secrets", h.Driver) - assert.Equal(t, "ns/secret-name", h.Handle) - assert.Equal(t, map[string]string{"k": "v"}, h.Metadata) -} - -func TestProviderToProto_OmitsZeroCredentialExpiry(t *testing.T) { - result := ProviderToProto(&types.Provider{Spec: types.ProviderSpec{ - CredentialExpiresAt: map[string]time.Time{"token": {}}, - }}) - - assert.NotContains(t, result.CredentialExpirationTimes, "token") -} - -func TestProviderFromProto_DeepCopyCredentialHandles(t *testing.T) { - proto := &dm.Provider{ - Metadata: &dm.ObjectMeta{Name: "deep-copy-test"}, - Type: "test", - CredentialHandles: map[string]*dm.CredentialHandle{ - "key": { - Driver: "vault", - Handle: "secret/test", - Metadata: map[string]string{"version": "1"}, - }, - }, - } - - result := ProviderFromProto(proto) - require.Len(t, result.Spec.CredentialHandles, 1) - - proto.CredentialHandles["key"].Metadata["version"] = "mutated" - proto.CredentialHandles["key"].Driver = "mutated" - - assert.Equal(t, "1", result.Spec.CredentialHandles["key"].Metadata["version"]) - assert.Equal(t, "vault", result.Spec.CredentialHandles["key"].Driver) -} - -func TestProviderToProto_DeepCopyCredentialHandles(t *testing.T) { - provider := &types.Provider{ - Name: "deep-copy-test", - Type: "test", - Spec: types.ProviderSpec{ - CredentialHandles: map[string]types.CredentialHandle{ - "token": { - Driver: "k8s", - Handle: "ns/secret", - Metadata: map[string]string{"k": "v"}, - }, - }, - }, - } - - result := ProviderToProto(provider) - require.Len(t, result.CredentialHandles, 1) - - provider.Spec.CredentialHandles["token"] = types.CredentialHandle{ - Driver: "mutated", Handle: "mutated", Metadata: map[string]string{"k": "mutated"}, - } - - assert.Equal(t, "k8s", result.CredentialHandles["token"].Driver) - assert.Equal(t, "ns/secret", result.CredentialHandles["token"].Handle) - assert.Equal(t, "v", result.CredentialHandles["token"].Metadata["k"]) -} - -func TestProviderRoundTrip(t *testing.T) { - original := &types.Provider{ - ID: "rt-1", - Name: "roundtrip", - Type: "gitlab", - ResourceVersion: 3, - Workspace: "default", - Labels: map[string]string{"team": "infra"}, - Spec: types.ProviderSpec{ - Config: map[string]string{"url": "https://gitlab.com"}, - ProfileWorkspace: "shared", - CredentialHandles: map[string]types.CredentialHandle{ - "pat": {Driver: "vault", Handle: "secret/gitlab", Metadata: map[string]string{"ver": "1"}}, - }, - }, - } - - proto := ProviderToProto(original) - back := ProviderFromProto(proto) - - assert.Equal(t, original.ID, back.ID) - assert.Equal(t, original.Name, back.Name) - assert.Equal(t, original.Type, back.Type) - assert.Equal(t, original.Workspace, back.Workspace) - assert.Equal(t, original.Labels, back.Labels) - assert.Equal(t, original.Spec.Config, back.Spec.Config) - assert.Equal(t, original.Spec.ProfileWorkspace, back.Spec.ProfileWorkspace) - assert.Equal(t, original.Spec.CredentialHandles, back.Spec.CredentialHandles) -} diff --git a/sdk/go/openshell/v1/internal/converter/refresh.go b/sdk/go/openshell/v1/internal/converter/refresh.go deleted file mode 100644 index b638050347..0000000000 --- a/sdk/go/openshell/v1/internal/converter/refresh.go +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" -) - -// --- RefreshStrategy enum mapping --- - -// RefreshStrategyFromProto converts a proto ProviderCredentialRefreshStrategy to an SDK RefreshStrategy. -func RefreshStrategyFromProto(s pb.ProviderCredentialRefreshStrategy) types.RefreshStrategy { - switch s { - case pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_STATIC: - return types.RefreshStrategyStatic - case pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_EXTERNAL: - return types.RefreshStrategyExternal - case pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN: - return types.RefreshStrategyOAuth2RefreshToken - case pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_CLIENT_CREDENTIALS: - return types.RefreshStrategyOAuth2ClientCredentials - case pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_GOOGLE_SERVICE_ACCOUNT_JWT: - return types.RefreshStrategyGoogleServiceAccountJWT - case pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_AWS_STS_ASSUME_ROLE: - return types.RefreshStrategyAWSStsAssumeRole - default: - return types.RefreshStrategy("") - } -} - -// --- RefreshRecoveryAction enum mapping --- - -// RefreshRecoveryActionFromProto converts a proto recovery action to the curated SDK type. -func RefreshRecoveryActionFromProto(a pb.ProviderCredentialRefreshRecoveryAction) types.RefreshRecoveryAction { - switch a { - case pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY: - return types.RefreshRecoveryActionRetry - case pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE: - return types.RefreshRecoveryActionReauthorize - case pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION: - return types.RefreshRecoveryActionFixConfiguration - case pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE: - return types.RefreshRecoveryActionInvestigate - default: - return types.RefreshRecoveryActionUnspecified - } -} - -// RefreshStrategyToProto converts an SDK RefreshStrategy to a proto ProviderCredentialRefreshStrategy. -func RefreshStrategyToProto(s types.RefreshStrategy) pb.ProviderCredentialRefreshStrategy { - switch s { - case types.RefreshStrategyStatic: - return pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_STATIC - case types.RefreshStrategyExternal: - return pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_EXTERNAL - case types.RefreshStrategyOAuth2RefreshToken: - return pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN - case types.RefreshStrategyOAuth2ClientCredentials: - return pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_CLIENT_CREDENTIALS - case types.RefreshStrategyGoogleServiceAccountJWT: - return pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_GOOGLE_SERVICE_ACCOUNT_JWT - case types.RefreshStrategyAWSStsAssumeRole: - return pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_AWS_STS_ASSUME_ROLE - default: - return pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED - } -} - -// --- RefreshStatus --- - -// RefreshStatusFromProto converts a proto ProviderCredentialRefreshStatus to an SDK RefreshStatus. -func RefreshStatusFromProto(s *pb.ProviderCredentialRefreshStatus) *types.RefreshStatus { - if s == nil { - return nil - } - return &types.RefreshStatus{ - Provider: s.GetProvider(), - ProviderID: s.GetProviderId(), - CredentialKey: s.GetCredentialKey(), - Strategy: RefreshStrategyFromProto(s.GetStrategy()), - Status: s.GetStatus(), - ExpiresAt: TimeFromProto(s.GetExpirationTime()), - NextRefreshAt: TimeFromProto(s.GetNextRefreshTime()), - LastRefreshAt: TimeFromProto(s.GetLastRefreshTime()), - LastError: s.GetLastError(), - RecoveryAction: RefreshRecoveryActionFromProto(s.GetRecoveryAction()), - FailureCode: s.GetFailureCode(), - ProviderErrorSubtype: s.GetProviderErrorSubtype(), - LastErrorAt: TimeFromProto(s.GetLastErrorTime()), - } -} - -// --- RefreshConfig --- - -// RefreshConfigToProto converts an SDK RefreshConfig to a proto ConfigureProviderRefreshRequest. -// Material and SecretMaterialKeys are deep-copied. -func RefreshConfigToProto(c *types.RefreshConfig) *pb.ConfigureProviderRefreshRequest { - if c == nil { - return nil - } - - result := &pb.ConfigureProviderRefreshRequest{ - Provider: c.Provider, - CredentialKey: c.CredentialKey, - Strategy: RefreshStrategyToProto(c.Strategy), - Material: CopyStringMap(c.Material), - SecretMaterialKeys: CopyStringSlice(c.SecretMaterialKeys), - } - - if c.ExpiresAt != nil { - result.ExpirationTime = TimestampFromTime(*c.ExpiresAt) - } - - return result -} diff --git a/sdk/go/openshell/v1/internal/converter/refresh_test.go b/sdk/go/openshell/v1/internal/converter/refresh_test.go deleted file mode 100644 index 9d94889a38..0000000000 --- a/sdk/go/openshell/v1/internal/converter/refresh_test.go +++ /dev/null @@ -1,212 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "math" - "testing" - "time" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// --- RefreshStrategy --- - -func TestRefreshStrategyFromProto(t *testing.T) { - tests := []struct { - proto pb.ProviderCredentialRefreshStrategy - want v1.RefreshStrategy - }{ - {pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_STATIC, v1.RefreshStrategyStatic}, - {pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_EXTERNAL, v1.RefreshStrategyExternal}, - {pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN, v1.RefreshStrategyOAuth2RefreshToken}, - {pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_CLIENT_CREDENTIALS, v1.RefreshStrategyOAuth2ClientCredentials}, - {pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_GOOGLE_SERVICE_ACCOUNT_JWT, v1.RefreshStrategyGoogleServiceAccountJWT}, - {pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_AWS_STS_ASSUME_ROLE, v1.RefreshStrategyAWSStsAssumeRole}, - {pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED, v1.RefreshStrategy("")}, - } - for _, tt := range tests { - t.Run(tt.proto.String(), func(t *testing.T) { - assert.Equal(t, tt.want, RefreshStrategyFromProto(tt.proto)) - }) - } -} - -func TestRefreshRecoveryActionFromProto(t *testing.T) { - tests := []struct { - proto pb.ProviderCredentialRefreshRecoveryAction - want v1.RefreshRecoveryAction - }{ - {pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED, v1.RefreshRecoveryActionUnspecified}, - {pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY, v1.RefreshRecoveryActionRetry}, - {pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE, v1.RefreshRecoveryActionReauthorize}, - {pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION, v1.RefreshRecoveryActionFixConfiguration}, - {pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE, v1.RefreshRecoveryActionInvestigate}, - {pb.ProviderCredentialRefreshRecoveryAction(999), v1.RefreshRecoveryActionUnspecified}, - } - for _, tt := range tests { - assert.Equal(t, tt.want, RefreshRecoveryActionFromProto(tt.proto)) - } -} - -func TestRefreshStrategyToProto(t *testing.T) { - tests := []struct { - sdk v1.RefreshStrategy - want pb.ProviderCredentialRefreshStrategy - }{ - {v1.RefreshStrategyStatic, pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_STATIC}, - {v1.RefreshStrategyExternal, pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_EXTERNAL}, - {v1.RefreshStrategyOAuth2RefreshToken, pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN}, - {v1.RefreshStrategyOAuth2ClientCredentials, pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_CLIENT_CREDENTIALS}, - {v1.RefreshStrategyGoogleServiceAccountJWT, pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_GOOGLE_SERVICE_ACCOUNT_JWT}, - {v1.RefreshStrategyAWSStsAssumeRole, pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_AWS_STS_ASSUME_ROLE}, - {v1.RefreshStrategy(""), pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED}, - {v1.RefreshStrategy("Unknown"), pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED}, - } - for _, tt := range tests { - t.Run(string(tt.sdk), func(t *testing.T) { - assert.Equal(t, tt.want, RefreshStrategyToProto(tt.sdk)) - }) - } -} - -// --- RefreshStatus --- - -func TestRefreshStatusFromProto(t *testing.T) { - proto := &pb.ProviderCredentialRefreshStatus{ - Provider: "anthropic", - ProviderId: "prov-1", - CredentialKey: "API_KEY", - Strategy: pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN, - Status: "active", - ExpirationTime: TimestampFromMillis(1700000000000), - NextRefreshTime: TimestampFromMillis(1699999000000), - LastRefreshTime: TimestampFromMillis(1699998000000), - LastError: "none", - RecoveryAction: pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE, - FailureCode: "oauth_invalid_grant", - ProviderErrorSubtype: "invalid_rapt", - LastErrorTime: TimestampFromMillis(1699997000000), - } - - status := RefreshStatusFromProto(proto) - - require.NotNil(t, status) - assert.Equal(t, "anthropic", status.Provider) - assert.Equal(t, "prov-1", status.ProviderID) - assert.Equal(t, "API_KEY", status.CredentialKey) - assert.Equal(t, v1.RefreshStrategyOAuth2RefreshToken, status.Strategy) - assert.Equal(t, "active", status.Status) - assert.Equal(t, TimeFromMillis(1700000000000), status.ExpiresAt) - assert.Equal(t, TimeFromMillis(1699999000000), status.NextRefreshAt) - assert.Equal(t, TimeFromMillis(1699998000000), status.LastRefreshAt) - assert.Equal(t, "none", status.LastError) - assert.Equal(t, v1.RefreshRecoveryActionReauthorize, status.RecoveryAction) - assert.Equal(t, "oauth_invalid_grant", status.FailureCode) - assert.Equal(t, "invalid_rapt", status.ProviderErrorSubtype) - assert.Equal(t, TimeFromMillis(1699997000000), status.LastErrorAt) -} - -func TestRefreshStatusFromProto_Nil(t *testing.T) { - status := RefreshStatusFromProto(nil) - assert.Nil(t, status) -} - -func TestRefreshStatusFromProto_ZeroTimestamps(t *testing.T) { - proto := &pb.ProviderCredentialRefreshStatus{ - Provider: "test", - CredentialKey: "KEY", - Strategy: pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_STATIC, - } - - status := RefreshStatusFromProto(proto) - - require.NotNil(t, status) - assert.Equal(t, v1.RefreshStrategyStatic, status.Strategy) - assert.True(t, status.ExpiresAt.IsZero()) - assert.True(t, status.NextRefreshAt.IsZero()) - assert.True(t, status.LastRefreshAt.IsZero()) - assert.True(t, status.LastErrorAt.IsZero()) - assert.Equal(t, v1.RefreshRecoveryActionUnspecified, status.RecoveryAction) -} - -func TestRefreshStatusFromProto_ParkedRefreshHasNoNextTime(t *testing.T) { - proto := &pb.ProviderCredentialRefreshStatus{ - Provider: "test", - CredentialKey: "KEY", - NextRefreshTime: nil, - RecoveryAction: pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE, - } - - status := RefreshStatusFromProto(proto) - - require.NotNil(t, status) - assert.True(t, status.NextRefreshAt.IsZero()) - assert.Equal(t, v1.RefreshRecoveryActionReauthorize, status.RecoveryAction) - assert.False(t, TimeFromMillis(math.MaxInt64).IsZero()) -} - -// --- RefreshConfig --- - -func TestRefreshConfigToProto(t *testing.T) { - expiresAt := time.Unix(1700000000, 0) - config := &v1.RefreshConfig{ - Provider: "anthropic", - CredentialKey: "API_KEY", - Strategy: v1.RefreshStrategyOAuth2ClientCredentials, - Material: map[string]string{ - "client_id": "my-id", - "client_secret": "my-secret", - }, - SecretMaterialKeys: []string{"client_secret"}, - ExpiresAt: &expiresAt, - } - - proto := RefreshConfigToProto(config) - - require.NotNil(t, proto) - assert.Equal(t, "anthropic", proto.Provider) - assert.Equal(t, "API_KEY", proto.CredentialKey) - assert.Equal(t, pb.ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_CLIENT_CREDENTIALS, proto.Strategy) - - // Material is deep-copied - require.Len(t, proto.Material, 2) - assert.Equal(t, "my-id", proto.Material["client_id"]) - assert.Equal(t, "my-secret", proto.Material["client_secret"]) - - // Verify deep copy by mutating original - config.Material["client_id"] = "mutated" - assert.Equal(t, "my-id", proto.Material["client_id"], "material must be deep copied") - - assert.Equal(t, []string{"client_secret"}, proto.SecretMaterialKeys) - - // Verify SecretMaterialKeys deep copy - config.SecretMaterialKeys[0] = "mutated" - assert.Equal(t, "client_secret", proto.SecretMaterialKeys[0], "secret keys must be deep copied") - - // ExpiresAt conversion - require.NotNil(t, proto.ExpirationTime) - assert.Equal(t, MillisFromTime(expiresAt), MillisFromProto(proto.ExpirationTime)) -} - -func TestRefreshConfigToProto_NilExpiresAt(t *testing.T) { - config := &v1.RefreshConfig{ - Provider: "test", - CredentialKey: "KEY", - Strategy: v1.RefreshStrategyStatic, - } - - proto := RefreshConfigToProto(config) - - require.NotNil(t, proto) - assert.Nil(t, proto.ExpirationTime) -} - -func TestRefreshConfigToProto_Nil(t *testing.T) { - proto := RefreshConfigToProto(nil) - assert.Nil(t, proto) -} diff --git a/sdk/go/openshell/v1/internal/converter/sandbox.go b/sdk/go/openshell/v1/internal/converter/sandbox.go deleted file mode 100644 index e679fba176..0000000000 --- a/sdk/go/openshell/v1/internal/converter/sandbox.go +++ /dev/null @@ -1,618 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "fmt" - "slices" - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/structpb" -) - -// SandboxFromProto converts a proto Sandbox to an SDK Sandbox. -func SandboxFromProto(s *pb.Sandbox) *types.Sandbox { - if s == nil { - return nil - } - - result := &types.Sandbox{HostKeyFingerprint: s.GetHostKeyFingerprint()} - - if m := s.GetMetadata(); m != nil { - result.ID = m.GetId() - result.Name = m.GetName() - result.CreatedAt = TimeFromProto(m.GetCreatedTime()) - result.Labels = CopyStringMap(m.GetLabels()) - result.Annotations = CopyStringMap(m.GetAnnotations()) - result.ResourceVersion = m.GetResourceVersion() - result.Workspace = m.GetWorkspace() - result.DeletionTimestamp = TimePtrFromProto(m.GetDeletionTime()) - } - - if provenance := s.GetCreatedFromWorkloadTemplate(); provenance != nil { - result.CreatedFromWorkloadTemplate = &types.SandboxWorkloadTemplateProvenance{ - Name: provenance.GetName(), - ResourceVersion: provenance.GetResourceVersion(), - } - } - - if spec := s.GetSpec(); spec != nil { - result.Spec = sandboxSpecFromProto(spec) - } - - if status := s.GetStatus(); status != nil { - result.Status = sandboxStatusFromProto(status) - } else { - result.Status.Phase = types.SandboxUnknown - } - - return result -} - -func sandboxSpecFromProto(spec *pb.SandboxSpec) types.SandboxSpec { - result := types.SandboxSpec{ - LogLevel: spec.GetLogLevel(), - Environment: CopyStringMap(spec.GetEnvironment()), - Providers: CopyStringSlice(spec.GetProviders()), - Policy: SandboxPolicyFromProto(spec.GetPolicy()), - RestartPolicy: SandboxRestartPolicyFromProto(spec.GetRestartPolicy()), - } - - if tmpl := spec.GetTemplate(); tmpl != nil { - t := &types.SandboxTemplate{ - Image: tmpl.GetImage(), - RuntimeClassName: tmpl.GetRuntimeClassName(), - AgentSocket: tmpl.GetAgentSocket(), - Labels: CopyStringMap(tmpl.GetLabels()), - Annotations: CopyStringMap(tmpl.GetAnnotations()), - Environment: CopyStringMap(tmpl.GetEnvironment()), - UserNamespaces: CopyBoolPtr(tmpl.UserNamespaces), - } - if res := tmpl.GetResources(); res != nil { - t.Resources = res.AsMap() - } - if dc := tmpl.GetDriverConfig(); dc != nil { - t.DriverConfig = dc.AsMap() - } - result.Template = t - } - - if rr := spec.GetResourceRequirements(); rr != nil { - if gpu := rr.GetGpu(); gpu != nil { - result.GPU = true - if gpu.Count != nil { - result.GPUCount = gpu.Count - } - } - } - result.Command = CopyStringSlice(spec.GetCommand()) - result.TTY = spec.GetTty() - - return result -} - -func sandboxStatusFromProto(status *pb.SandboxStatus) types.SandboxStatus { - result := types.SandboxStatus{ - AgentPod: status.GetAgentPod(), - AgentFd: status.GetAgentFd(), - SandboxFd: status.GetSandboxFd(), - Phase: SandboxPhaseFromProto(status.GetPhase()), - CurrentPolicyVersion: status.GetCurrentPolicyVersion(), - } - - for _, c := range status.GetConditions() { - result.Conditions = append(result.Conditions, types.SandboxCondition{ - Type: c.GetType(), - Status: c.GetStatus(), - Reason: c.GetReason(), - Message: c.GetMessage(), - LastTransitionTime: TimestampStringFromProto(c.GetTransitionTime()), - }) - } - for _, endpoint := range status.GetEndpointStatuses() { - result.EndpointStatuses = append(result.EndpointStatuses, types.EndpointStatus{ - EndpointID: endpoint.GetEndpointId(), - Host: endpoint.GetHost(), - Ports: slices.Clone(endpoint.GetPorts()), - Path: endpoint.GetPath(), - LastResult: endpointResultFromProto(endpoint.GetLastResult()), - LastReportedAt: TimestampStringFromProto(endpoint.GetLastReportedTime()), - LastFailure: endpointFailureFromProto(endpoint.GetLastFailure()), - }) - } - if observation := status.GetLastTokenGrantFailure(); observation != nil { - result.LastTokenGrantFailure = &types.TokenGrantFailureObservation{ - DestinationHost: observation.GetDestinationHost(), - DestinationPort: observation.GetDestinationPort(), - Failure: endpointFailureFromProto(observation.GetFailure()), - } - } - result.ExitCode = CopyInt32Ptr(status.ExitCode) - if admission := status.GetConfigurationAdmission(); admission != nil { - state := types.ConfigurationAdmissionUnknown - switch admission.GetState() { - case pb.ConfigurationAdmissionState_CONFIGURATION_ADMISSION_STATE_PENDING: - state = types.ConfigurationAdmissionPending - case pb.ConfigurationAdmissionState_CONFIGURATION_ADMISSION_STATE_ACCEPTED: - state = types.ConfigurationAdmissionAccepted - case pb.ConfigurationAdmissionState_CONFIGURATION_ADMISSION_STATE_REJECTED: - state = types.ConfigurationAdmissionRejected - } - result.ConfigurationAdmission = &types.SandboxConfigurationAdmission{ - State: state, - PolicyVersion: admission.GetPolicyVersion(), - PolicyHash: admission.GetPolicyHash(), - ConfigRevision: admission.GetConfigRevision(), - ProviderEnvRevision: admission.GetProviderEnvRevision(), - Error: admission.GetError(), - } - } - result.RestartCount = status.GetRestartCount() - result.NextRestartAtMs = MillisFromProto(status.GetNextRestartTime()) - result.MainProcessStartedAtMs = MillisFromProto(status.GetMainProcessStartedTime()) - - return result -} - -func endpointResultFromProto(result pb.EndpointResult) types.EndpointResult { - switch result { - case pb.EndpointResult_ENDPOINT_RESULT_NO_OBSERVED_EXCHANGE: - return types.EndpointNoObservedExchange - case pb.EndpointResult_ENDPOINT_RESULT_HTTP_RESPONSE_RECEIVED: - return types.EndpointHTTPResponseReceived - case pb.EndpointResult_ENDPOINT_RESULT_POLICY_DENIED: - return types.EndpointPolicyDenied - case pb.EndpointResult_ENDPOINT_RESULT_CREDENTIAL_UNAVAILABLE: - return types.EndpointCredentialUnavailable - case pb.EndpointResult_ENDPOINT_RESULT_TLS_FAILED: - return types.EndpointTLSFailed - case pb.EndpointResult_ENDPOINT_RESULT_TRANSPORT_FAILED: - return types.EndpointTransportFailed - case pb.EndpointResult_ENDPOINT_RESULT_UPSTREAM_REJECTED: - return types.EndpointUpstreamRejected - default: - // Unknown wire values must not imply an observation or a successful call. - return types.EndpointUnspecified - } -} - -func endpointFailureFromProto(failure *pb.EndpointFailure) *types.EndpointFailure { - if failure == nil { - return nil - } - return &types.EndpointFailure{ - Domain: endpointFailureDomainFromProto(failure.GetDomain()), - Code: failure.GetCode(), - RecoveryAction: RefreshRecoveryActionFromProto(failure.GetRecoveryAction()), - Provider: failure.GetProvider(), - CredentialKey: failure.GetCredentialKey(), - Stage: failure.GetStage(), - UpstreamHTTPStatus: CopyUint32Ptr(failure.UpstreamHttpStatus), - RequestID: failure.GetRequestId(), - } -} - -func endpointFailureDomainFromProto(domain pb.EndpointFailureDomain) types.EndpointFailureDomain { - switch domain { - case pb.EndpointFailureDomain_ENDPOINT_FAILURE_DOMAIN_TOKEN_GRANT: - return types.EndpointFailureDomainTokenGrant - default: - // Unknown wire values must not select a closed code set. - return types.EndpointFailureDomainUnspecified - } -} - -// SandboxPhaseFromProto converts a proto SandboxPhase to an SDK SandboxPhase. -func SandboxPhaseFromProto(phase pb.SandboxPhase) types.SandboxPhase { - switch phase { - case pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING: - return types.SandboxProvisioning - case pb.SandboxPhase_SANDBOX_PHASE_READY: - return types.SandboxReady - case pb.SandboxPhase_SANDBOX_PHASE_ERROR: - return types.SandboxError - case pb.SandboxPhase_SANDBOX_PHASE_DELETING: - return types.SandboxDeleting - case pb.SandboxPhase_SANDBOX_PHASE_UNKNOWN: - return types.SandboxUnknown - case pb.SandboxPhase_SANDBOX_PHASE_STOPPING: - return types.SandboxStopping - case pb.SandboxPhase_SANDBOX_PHASE_STOPPED: - return types.SandboxStopped - case pb.SandboxPhase_SANDBOX_PHASE_STARTING: - return types.SandboxStarting - case pb.SandboxPhase_SANDBOX_PHASE_COMPLETED: - return types.SandboxCompleted - case pb.SandboxPhase_SANDBOX_PHASE_SUSPENDING: - return types.SandboxSuspending - case pb.SandboxPhase_SANDBOX_PHASE_SUSPENDED: - return types.SandboxSuspended - default: - return types.SandboxUnknown - } -} - -// SandboxPhaseToProto converts an SDK SandboxPhase to a proto SandboxPhase. -func SandboxPhaseToProto(phase types.SandboxPhase) pb.SandboxPhase { - switch phase { - case types.SandboxProvisioning: - return pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING - case types.SandboxReady: - return pb.SandboxPhase_SANDBOX_PHASE_READY - case types.SandboxError: - return pb.SandboxPhase_SANDBOX_PHASE_ERROR - case types.SandboxDeleting: - return pb.SandboxPhase_SANDBOX_PHASE_DELETING - case types.SandboxUnknown: - return pb.SandboxPhase_SANDBOX_PHASE_UNKNOWN - case types.SandboxStopping: - return pb.SandboxPhase_SANDBOX_PHASE_STOPPING - case types.SandboxStopped: - return pb.SandboxPhase_SANDBOX_PHASE_STOPPED - case types.SandboxStarting: - return pb.SandboxPhase_SANDBOX_PHASE_STARTING - case types.SandboxCompleted: - return pb.SandboxPhase_SANDBOX_PHASE_COMPLETED - case types.SandboxSuspending: - return pb.SandboxPhase_SANDBOX_PHASE_SUSPENDING - case types.SandboxSuspended: - return pb.SandboxPhase_SANDBOX_PHASE_SUSPENDED - default: - return pb.SandboxPhase_SANDBOX_PHASE_UNKNOWN - } -} - -// SandboxToProto converts an SDK Sandbox to a proto Sandbox. -func SandboxToProto(s *types.Sandbox) *pb.Sandbox { - if s == nil { - return nil - } - - return &pb.Sandbox{ - Metadata: &dm.ObjectMeta{ - Id: s.ID, - Name: s.Name, - CreatedTime: TimestampFromTime(s.CreatedAt), - Labels: CopyStringMap(s.Labels), - Annotations: CopyStringMap(s.Annotations), - ResourceVersion: s.ResourceVersion, - Workspace: s.Workspace, - DeletionTime: TimestampFromTimePtr(s.DeletionTimestamp), - }, - Spec: SandboxSpecToProto(&s.Spec), - } -} - -// SandboxSpecToProto converts an SDK SandboxSpec to a proto SandboxSpec. -func SandboxSpecToProto(spec *types.SandboxSpec) *pb.SandboxSpec { - if spec == nil { - return nil - } - - result := &pb.SandboxSpec{ - LogLevel: spec.LogLevel, - Environment: CopyStringMap(spec.Environment), - Providers: CopyStringSlice(spec.Providers), - Policy: SandboxPolicyToProto(spec.Policy), - } - - if spec.Template != nil { - tmpl := &pb.SandboxTemplate{ - Image: spec.Template.Image, - RuntimeClassName: spec.Template.RuntimeClassName, - AgentSocket: spec.Template.AgentSocket, - Labels: CopyStringMap(spec.Template.Labels), - Annotations: CopyStringMap(spec.Template.Annotations), - Environment: CopyStringMap(spec.Template.Environment), - UserNamespaces: CopyBoolPtr(spec.Template.UserNamespaces), - } - if spec.Template.Resources != nil { - // Non-JSON-compatible values (e.g., chan, func) are silently dropped. - // Round-trip data from structpb.AsMap is always re-serializable. - s, err := structpb.NewStruct(spec.Template.Resources) - if err == nil { - tmpl.Resources = s - } - } - if spec.Template.DriverConfig != nil { - s, err := structpb.NewStruct(spec.Template.DriverConfig) - if err == nil { - tmpl.DriverConfig = s - } - } - result.Template = tmpl - } - - if spec.GPU || spec.GPUCount != nil { - result.ResourceRequirements = &pb.ResourceRequirements{ - Gpu: &pb.GpuResourceRequirements{ - Count: spec.GPUCount, - }, - } - } - - result.Command = CopyStringSlice(spec.Command) - result.Tty = spec.TTY - result.RestartPolicy = SandboxRestartPolicyToProto(spec.RestartPolicy) - - return result -} - -// SandboxRestartPolicyFromProto converts the restart policy to the curated SDK type. -func SandboxRestartPolicyFromProto(policy pb.SandboxRestartPolicy) types.SandboxRestartPolicy { - switch policy { - case pb.SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ON_FAILURE: - return types.SandboxRestartOnFailure - case pb.SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ALWAYS: - return types.SandboxRestartAlways - default: - return types.SandboxRestartNever - } -} - -// SandboxRestartPolicyToProto converts the curated SDK restart policy to protobuf. -func SandboxRestartPolicyToProto(policy types.SandboxRestartPolicy) pb.SandboxRestartPolicy { - switch policy { - case types.SandboxRestartOnFailure: - return pb.SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ON_FAILURE - case types.SandboxRestartAlways: - return pb.SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ALWAYS - default: - return pb.SandboxRestartPolicy_SANDBOX_RESTART_POLICY_NEVER - } -} - -// SandboxSpecToProtoChecked converts an SDK SandboxSpec and reports values -// that protobuf Struct cannot represent instead of silently dropping them. -func SandboxSpecToProtoChecked(spec *types.SandboxSpec) (*pb.SandboxSpec, error) { - result := SandboxSpecToProto(spec) - if spec == nil { - return result, nil - } - policy, err := SandboxPolicyToProtoChecked(spec.Policy) - if err != nil { - return nil, fmt.Errorf("policy: %w", err) - } - result.Policy = policy - if spec.Template == nil { - return result, nil - } - if spec.Template.Resources != nil { - resources, err := structpb.NewStruct(spec.Template.Resources) - if err != nil { - return nil, fmt.Errorf("template resources: %w", err) - } - result.Template.Resources = resources - } - if spec.Template.DriverConfig != nil { - driverConfig, err := structpb.NewStruct(spec.Template.DriverConfig) - if err != nil { - return nil, fmt.Errorf("template driver config: %w", err) - } - result.Template.DriverConfig = driverConfig - } - return result, nil -} - -// SandboxWorkloadTemplateFromProto converts a reusable template proto to an SDK template. -func SandboxWorkloadTemplateFromProto(t *pb.SandboxWorkloadTemplate) *types.SandboxWorkloadTemplate { - if t == nil { - return nil - } - - result := &types.SandboxWorkloadTemplate{} - if m := t.GetMetadata(); m != nil { - result.ID = m.GetId() - result.Name = m.GetName() - result.CreatedAt = TimeFromProto(m.GetCreatedTime()) - result.Labels = CopyStringMap(m.GetLabels()) - result.Annotations = CopyStringMap(m.GetAnnotations()) - result.ResourceVersion = m.GetResourceVersion() - result.Workspace = m.GetWorkspace() - result.DeletionTimestamp = TimePtrFromProto(m.GetDeletionTime()) - } - if spec := t.GetSpec(); spec != nil { - result.Spec = SandboxWorkloadTemplateSpecFromProto(spec) - } - return result -} - -// SandboxWorkloadTemplateSpecFromProto converts a reusable template spec proto. -func SandboxWorkloadTemplateSpecFromProto(spec *pb.SandboxWorkloadTemplateSpec) types.SandboxWorkloadTemplateSpec { - result := types.SandboxWorkloadTemplateSpec{} - if spec == nil { - return result - } - result.Workload = SandboxWorkloadConfigFromProto(spec.GetWorkload()) - result.DesiredServiceLevel = SandboxServiceLevelFromProto(spec.GetDesiredServiceLevel()) - if dc := spec.GetDriverConfig(); dc != nil { - result.DriverConfig = dc.AsMap() - } - return result -} - -// SandboxWorkloadConfigFromProto converts a portable workload proto. -func SandboxWorkloadConfigFromProto(workload *pb.SandboxWorkloadConfig) *types.SandboxWorkloadConfig { - if workload == nil { - return nil - } - return &types.SandboxWorkloadConfig{ - Image: workload.GetImage(), - Environment: CopyStringMap(workload.GetEnvironment()), - Resources: SandboxResourcesFromProto(workload.GetResources()), - } -} - -// SandboxResourcesFromProto converts portable resource requirements. -func SandboxResourcesFromProto(resources *pb.SandboxResources) *types.SandboxResources { - if resources == nil { - return nil - } - return &types.SandboxResources{ - CPU: resources.GetCpu(), - Memory: resources.GetMemory(), - GPU: sandboxResourceGpuFromProto(resources), - } -} - -// SandboxServiceLevelFromProto converts template service-level hints. -func SandboxServiceLevelFromProto(level *pb.SandboxServiceLevel) *types.SandboxServiceLevel { - if level == nil { - return nil - } - return &types.SandboxServiceLevel{ - Startup: SandboxStartupFromProto(level.GetStartup()), - } -} - -// SandboxStartupFromProto converts startup service-level hints. -func SandboxStartupFromProto(startup *pb.SandboxStartup) *types.SandboxStartup { - if startup == nil { - return nil - } - return &types.SandboxStartup{ - ReadyWithin: durationFromProto(startup.GetReadyWithin()), - MaxBurst: startup.GetMaxBurst(), - } -} - -// SandboxWorkloadTemplateToProto converts an SDK reusable template to proto. -func SandboxWorkloadTemplateToProto(t *types.SandboxWorkloadTemplate) *pb.SandboxWorkloadTemplate { - if t == nil { - return nil - } - return &pb.SandboxWorkloadTemplate{ - Metadata: &dm.ObjectMeta{ - Id: t.ID, - Name: t.Name, - CreatedTime: TimestampFromTime(t.CreatedAt), - Labels: CopyStringMap(t.Labels), - Annotations: CopyStringMap(t.Annotations), - ResourceVersion: t.ResourceVersion, - Workspace: t.Workspace, - DeletionTime: TimestampFromTimePtr(t.DeletionTimestamp), - }, - Spec: SandboxWorkloadTemplateSpecToProto(&t.Spec), - } -} - -// SandboxWorkloadTemplateSpecToProto converts an SDK reusable template spec to proto. -func SandboxWorkloadTemplateSpecToProto(spec *types.SandboxWorkloadTemplateSpec) *pb.SandboxWorkloadTemplateSpec { - if spec == nil { - return nil - } - result := &pb.SandboxWorkloadTemplateSpec{ - Workload: SandboxWorkloadConfigToProto(spec.Workload), - DesiredServiceLevel: SandboxServiceLevelToProto(spec.DesiredServiceLevel), - } - if spec.DriverConfig != nil { - if driverConfig, err := structpb.NewStruct(spec.DriverConfig); err == nil { - result.DriverConfig = driverConfig - } - } - return result -} - -// SandboxWorkloadConfigToProto converts an SDK portable workload to proto. -func SandboxWorkloadConfigToProto(workload *types.SandboxWorkloadConfig) *pb.SandboxWorkloadConfig { - if workload == nil { - return nil - } - return &pb.SandboxWorkloadConfig{ - Image: workload.Image, - Environment: CopyStringMap(workload.Environment), - Resources: SandboxResourcesToProto(workload.Resources), - } -} - -// SandboxResourcesToProto converts portable resource requirements. -func SandboxResourcesToProto(resources *types.SandboxResources) *pb.SandboxResources { - if resources == nil { - return nil - } - return &pb.SandboxResources{ - Cpu: resources.CPU, - Memory: resources.Memory, - Gpu: sandboxResourceGpuToProto(resources), - } -} - -func sandboxResourceGpuToProto(resources *types.SandboxResources) *pb.GpuResourceRequirements { - if resources == nil || resources.GPU == nil { - return nil - } - return &pb.GpuResourceRequirements{Count: CopyUint32Ptr(resources.GPU.Count)} -} - -func sandboxResourceGpuFromProto(resources *pb.SandboxResources) *types.SandboxGPURequirements { - if resources == nil || resources.GetGpu() == nil { - return nil - } - return &types.SandboxGPURequirements{Count: CopyUint32Ptr(resources.GetGpu().Count)} -} - -// SandboxServiceLevelToProto converts template service-level hints. -func SandboxServiceLevelToProto(level *types.SandboxServiceLevel) *pb.SandboxServiceLevel { - if level == nil { - return nil - } - return &pb.SandboxServiceLevel{ - Startup: SandboxStartupToProto(level.Startup), - } -} - -// SandboxStartupToProto converts startup service-level hints. -func SandboxStartupToProto(startup *types.SandboxStartup) *pb.SandboxStartup { - if startup == nil { - return nil - } - return &pb.SandboxStartup{ - ReadyWithin: durationToProto(startup.ReadyWithin), - MaxBurst: startup.MaxBurst, - } -} - -// SandboxWorkloadTemplateToProtoChecked converts an SDK reusable template and -// reports driver config values that protobuf Struct cannot represent. -func SandboxWorkloadTemplateToProtoChecked(t *types.SandboxWorkloadTemplate) (*pb.SandboxWorkloadTemplate, error) { - result := SandboxWorkloadTemplateToProto(t) - if t == nil { - return result, nil - } - if t.Spec.DriverConfig != nil { - driverConfig, err := structpb.NewStruct(t.Spec.DriverConfig) - if err != nil { - return nil, fmt.Errorf("driver config: %w", err) - } - result.Spec.DriverConfig = driverConfig - } - return result, nil -} - -// CopyUint32Ptr returns a copy of a *uint32 pointer. -func CopyUint32Ptr(p *uint32) *uint32 { - if p == nil { - return nil - } - v := *p - return &v -} - -func durationFromProto(d *durationpb.Duration) time.Duration { - if d == nil { - return 0 - } - return d.AsDuration() -} - -func durationToProto(d time.Duration) *durationpb.Duration { - if d == 0 { - return nil - } - return durationpb.New(d) -} diff --git a/sdk/go/openshell/v1/internal/converter/sandbox_test.go b/sdk/go/openshell/v1/internal/converter/sandbox_test.go deleted file mode 100644 index 756fd72338..0000000000 --- a/sdk/go/openshell/v1/internal/converter/sandbox_test.go +++ /dev/null @@ -1,818 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - "time" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - sandboxpb "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/structpb" - "google.golang.org/protobuf/types/known/timestamppb" -) - -func TestSandboxConfigurationAdmissionFromProto(t *testing.T) { - for _, tc := range []struct { - wire pb.ConfigurationAdmissionState - want v1.ConfigurationAdmissionState - }{ - {pb.ConfigurationAdmissionState_CONFIGURATION_ADMISSION_STATE_PENDING, v1.ConfigurationAdmissionPending}, - {pb.ConfigurationAdmissionState_CONFIGURATION_ADMISSION_STATE_ACCEPTED, v1.ConfigurationAdmissionAccepted}, - {pb.ConfigurationAdmissionState_CONFIGURATION_ADMISSION_STATE_REJECTED, v1.ConfigurationAdmissionRejected}, - {pb.ConfigurationAdmissionState(99), v1.ConfigurationAdmissionUnknown}, - } { - t.Run(string(tc.want), func(t *testing.T) { - wire := &pb.SandboxStatus{ConfigurationAdmission: &pb.SandboxConfigurationAdmission{ - State: tc.wire, PolicyVersion: 4, PolicyHash: "hash", ConfigRevision: 5, - ProviderEnvRevision: 6, Error: "invalid endpoint", - }} - got := sandboxStatusFromProto(wire) - assert.Equal(t, &v1.SandboxConfigurationAdmission{ - State: tc.want, PolicyVersion: 4, PolicyHash: "hash", ConfigRevision: 5, - ProviderEnvRevision: 6, Error: "invalid endpoint", - }, got.ConfigurationAdmission) - wire.ConfigurationAdmission.Error = "changed" - assert.Equal(t, "invalid endpoint", got.ConfigurationAdmission.Error) - }) - } - assert.Nil(t, sandboxStatusFromProto(&pb.SandboxStatus{}).ConfigurationAdmission) -} - -func TestSandboxFromProto(t *testing.T) { - userNS := true - gpuCount := uint32(2) - exitCode := int32(0) - proto := &pb.Sandbox{ - HostKeyFingerprint: "SHA256:expected", - Metadata: &dm.ObjectMeta{ - Id: "sb-1", - Name: "my-sandbox", - CreatedTime: TimestampFromMillis(1700000000000), - Labels: map[string]string{"env": "dev"}, - Annotations: map[string]string{"owner": "team-a"}, - ResourceVersion: 3, - Workspace: "prod", - DeletionTime: TimestampFromMillis(1700000060000), - }, - Spec: &pb.SandboxSpec{ - LogLevel: "debug", - Environment: map[string]string{"FOO": "bar"}, - Template: &pb.SandboxTemplate{ - Image: "nvidia/sandbox:latest", - RuntimeClassName: "kata", - AgentSocket: "/var/run/agent.sock", - Labels: map[string]string{"app": "test"}, - Annotations: map[string]string{"note": "hello"}, - Environment: map[string]string{"TMPL_VAR": "val"}, - UserNamespaces: &userNS, - Resources: func() *structpb.Struct { - s, _ := structpb.NewStruct(map[string]any{"cpu": "2", "memory": "4Gi"}) - return s - }(), - DriverConfig: func() *structpb.Struct { - s, _ := structpb.NewStruct(map[string]any{"runtime": "kata", "nested": map[string]any{"key": "val"}}) - return s - }(), - }, - Providers: []string{"claude", "github"}, - ResourceRequirements: &pb.ResourceRequirements{ - Gpu: &pb.GpuResourceRequirements{ - Count: &gpuCount, - }, - }, - Command: []string{"/opt/agent", "--serve"}, - Tty: false, - RestartPolicy: pb.SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ON_FAILURE, - }, - CreatedFromWorkloadTemplate: &pb.SandboxWorkloadTemplateProvenance{ - Name: "gpu-kata", - ResourceVersion: "7", - }, - Status: &pb.SandboxStatus{ - AgentPod: "agent-pod-xyz", - AgentFd: "fd-agent", - SandboxFd: "fd-sandbox", - Phase: pb.SandboxPhase_SANDBOX_PHASE_READY, - CurrentPolicyVersion: 7, - MainProcessInstanceId: "instance-1", - ExitCode: &exitCode, - RestartCount: 3, - NextRestartTime: TimestampFromMillis(1700000070000), - MainProcessStartedTime: TimestampFromMillis(1700000010000), - Conditions: []*pb.SandboxCondition{ - { - Type: "Ready", - Status: "True", - Reason: "AllGood", - Message: "Sandbox is ready", - TransitionTime: TimestampFromMillis(1704067200000), - }, - }, - }, - } - - s := SandboxFromProto(proto) - assert.Equal(t, "SHA256:expected", s.HostKeyFingerprint) - - require.NotNil(t, s) - assert.Equal(t, "sb-1", s.ID) - assert.Equal(t, "my-sandbox", s.Name) - assert.Equal(t, time.UnixMilli(1700000000000).UTC(), s.CreatedAt) - assert.Equal(t, map[string]string{"env": "dev"}, s.Labels) - assert.Equal(t, map[string]string{"owner": "team-a"}, s.Annotations) - assert.Equal(t, uint64(3), s.ResourceVersion) - assert.Equal(t, "prod", s.Workspace) - require.NotNil(t, s.DeletionTimestamp) - assert.Equal(t, time.UnixMilli(1700000060000).UTC(), *s.DeletionTimestamp) - require.NotNil(t, s.CreatedFromWorkloadTemplate) - assert.Equal(t, "gpu-kata", s.CreatedFromWorkloadTemplate.Name) - assert.Equal(t, "7", s.CreatedFromWorkloadTemplate.ResourceVersion) - - // Spec - assert.Equal(t, "debug", s.Spec.LogLevel) - assert.Equal(t, map[string]string{"FOO": "bar"}, s.Spec.Environment) - assert.Equal(t, []string{"claude", "github"}, s.Spec.Providers) - assert.True(t, s.Spec.GPU) - require.NotNil(t, s.Spec.GPUCount) - assert.Equal(t, uint32(2), *s.Spec.GPUCount) - assert.Equal(t, []string{"/opt/agent", "--serve"}, s.Spec.Command) - assert.False(t, s.Spec.TTY) - assert.Equal(t, v1.SandboxRestartOnFailure, s.Spec.RestartPolicy) - - // Template - require.NotNil(t, s.Spec.Template) - assert.Equal(t, "nvidia/sandbox:latest", s.Spec.Template.Image) - assert.Equal(t, "kata", s.Spec.Template.RuntimeClassName) - assert.Equal(t, "/var/run/agent.sock", s.Spec.Template.AgentSocket) - assert.Equal(t, map[string]string{"app": "test"}, s.Spec.Template.Labels) - assert.Equal(t, map[string]string{"note": "hello"}, s.Spec.Template.Annotations) - assert.Equal(t, map[string]string{"TMPL_VAR": "val"}, s.Spec.Template.Environment) - require.NotNil(t, s.Spec.Template.UserNamespaces) - assert.True(t, *s.Spec.Template.UserNamespaces) - assert.Equal(t, map[string]any{"cpu": "2", "memory": "4Gi"}, s.Spec.Template.Resources) - assert.Equal(t, "kata", s.Spec.Template.DriverConfig["runtime"]) - nested, ok := s.Spec.Template.DriverConfig["nested"].(map[string]any) - require.True(t, ok) - assert.Equal(t, "val", nested["key"]) - - // Status - assert.Equal(t, "agent-pod-xyz", s.Status.AgentPod) - assert.Equal(t, "fd-agent", s.Status.AgentFd) - assert.Equal(t, "fd-sandbox", s.Status.SandboxFd) - assert.Equal(t, v1.SandboxReady, s.Status.Phase) - assert.Equal(t, uint32(7), s.Status.CurrentPolicyVersion) - require.Len(t, s.Status.Conditions, 1) - assert.Equal(t, "Ready", s.Status.Conditions[0].Type) - assert.Equal(t, "True", s.Status.Conditions[0].Status) - assert.Equal(t, "AllGood", s.Status.Conditions[0].Reason) - assert.Equal(t, "Sandbox is ready", s.Status.Conditions[0].Message) - assert.Equal(t, "2024-01-01T00:00:00Z", s.Status.Conditions[0].LastTransitionTime) - require.NotNil(t, s.Status.ExitCode) - assert.Equal(t, int32(0), *s.Status.ExitCode) - assert.Equal(t, uint32(3), s.Status.RestartCount) - assert.Equal(t, int64(1700000070000), s.Status.NextRestartAtMs) - assert.Equal(t, int64(1700000010000), s.Status.MainProcessStartedAtMs) -} - -func TestSandboxFromProto_TemplateResourcesDeepCopy(t *testing.T) { - proto := &pb.Sandbox{ - Spec: &pb.SandboxSpec{ - Template: &pb.SandboxTemplate{ - Image: "img:v1", - Resources: func() *structpb.Struct { - s, _ := structpb.NewStruct(map[string]any{"cpu": "2"}) - return s - }(), - DriverConfig: func() *structpb.Struct { - s, _ := structpb.NewStruct(map[string]any{"runtime": "kata"}) - return s - }(), - }, - }, - } - - s := SandboxFromProto(proto) - require.NotNil(t, s) - - proto.Spec.Template.Resources.Fields["cpu"] = structpb.NewStringValue("MUTATED") - assert.Equal(t, "2", s.Spec.Template.Resources["cpu"], "Resources must be deep copied") - - proto.Spec.Template.DriverConfig.Fields["runtime"] = structpb.NewStringValue("MUTATED") - assert.Equal(t, "kata", s.Spec.Template.DriverConfig["runtime"], "DriverConfig must be deep copied") -} - -func TestSandboxFromProto_EndpointStatuses(t *testing.T) { - for _, tc := range []struct { - name string - status *pb.SandboxStatus - }{ - {name: "nil status"}, - {name: "nil endpoints", status: &pb.SandboxStatus{}}, - {name: "empty endpoints", status: &pb.SandboxStatus{EndpointStatuses: []*pb.EndpointStatus{}}}, - } { - t.Run(tc.name, func(t *testing.T) { - s := SandboxFromProto(&pb.Sandbox{Status: tc.status}) - require.NotNil(t, s) - assert.Empty(t, s.Status.EndpointStatuses) - }) - } - - input := &pb.Sandbox{Status: &pb.SandboxStatus{ - Phase: pb.SandboxPhase_SANDBOX_PHASE_READY, - Conditions: []*pb.SandboxCondition{{ - Type: "Ready", Status: "True", Reason: "AllGood", Message: "Sandbox is ready", - }}, - EndpointStatuses: []*pb.EndpointStatus{ - {EndpointId: "endpoint-one", Host: "tools.example.test", Ports: []uint32{443, 8443}, Path: "/mcp", LastResult: pb.EndpointResult_ENDPOINT_RESULT_TRANSPORT_FAILED, LastReportedTime: timestamppb.New(time.Date(2026, 9, 11, 10, 0, 0, 0, time.UTC))}, - {EndpointId: "endpoint-two", Host: "tools.example.test", Ports: []uint32{443}, Path: "/other", LastResult: pb.EndpointResult_ENDPOINT_RESULT_NO_OBSERVED_EXCHANGE}, - }, - }} - - s := SandboxFromProto(input) - require.NotNil(t, s) - require.Equal(t, []v1.EndpointStatus{ - {EndpointID: "endpoint-one", Host: "tools.example.test", Ports: []uint32{443, 8443}, Path: "/mcp", LastResult: v1.EndpointTransportFailed, LastReportedAt: "2026-09-11T10:00:00Z"}, - {EndpointID: "endpoint-two", Host: "tools.example.test", Ports: []uint32{443}, Path: "/other", LastResult: v1.EndpointNoObservedExchange}, - }, s.Status.EndpointStatuses) - assert.Equal(t, v1.SandboxReady, s.Status.Phase) - assert.Equal(t, []v1.SandboxCondition{{Type: "Ready", Status: "True", Reason: "AllGood", Message: "Sandbox is ready"}}, s.Status.Conditions) - - // A response and its SDK representation must not share endpoint or port storage. - input.Status.EndpointStatuses[0].Host = "changed.example.test" - input.Status.EndpointStatuses[0].Ports[0] = 80 - assert.Equal(t, "tools.example.test", s.Status.EndpointStatuses[0].Host) - assert.Equal(t, uint32(443), s.Status.EndpointStatuses[0].Ports[0]) - s.Status.EndpointStatuses[1].Path = "/changed" - s.Status.EndpointStatuses[1].Ports[0] = 8080 - assert.Equal(t, "/other", input.Status.EndpointStatuses[1].Path) - assert.Equal(t, uint32(443), input.Status.EndpointStatuses[1].Ports[0]) -} - -func TestSandboxFromProto_TokenGrantFailures(t *testing.T) { - upstreamStatus := uint32(409) - failure := &pb.EndpointFailure{ - Domain: pb.EndpointFailureDomain_ENDPOINT_FAILURE_DOMAIN_TOKEN_GRANT, - Code: "provider_authorization_required", - RecoveryAction: pb.ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE, - Provider: "github", - CredentialKey: "token", - Stage: "token_endpoint", - UpstreamHttpStatus: &upstreamStatus, - RequestId: "0b6f1f6e-7c55-4f1c-9a43-2d8f5b1f0e11", - } - input := &pb.Sandbox{Status: &pb.SandboxStatus{ - EndpointStatuses: []*pb.EndpointStatus{{EndpointId: "with-failure", LastFailure: failure}, {EndpointId: "without-failure"}}, - LastTokenGrantFailure: &pb.TokenGrantFailureObservation{ - DestinationHost: "api.example.test", - DestinationPort: 8443, - Failure: failure, - }, - }} - - status := SandboxFromProto(input).Status - wantStatus := uint32(409) - want := &v1.EndpointFailure{ - Domain: v1.EndpointFailureDomainTokenGrant, - Code: "provider_authorization_required", - RecoveryAction: v1.RefreshRecoveryActionReauthorize, - Provider: "github", - CredentialKey: "token", - Stage: "token_endpoint", - UpstreamHTTPStatus: &wantStatus, - RequestID: "0b6f1f6e-7c55-4f1c-9a43-2d8f5b1f0e11", - } - assert.Equal(t, want, status.EndpointStatuses[0].LastFailure) - assert.Nil(t, status.EndpointStatuses[1].LastFailure) - assert.Equal(t, &v1.TokenGrantFailureObservation{ - DestinationHost: "api.example.test", - DestinationPort: 8443, - Failure: want, - }, status.LastTokenGrantFailure) - - // The SDK values must not share the wire message's optional status. - upstreamStatus = 500 - assert.Equal(t, uint32(409), *status.LastTokenGrantFailure.Failure.UpstreamHTTPStatus) - - // Unknown wire domains must not select a closed code set. - failure.Domain = pb.EndpointFailureDomain(99) - input.Status.LastTokenGrantFailure.Failure = nil - status = SandboxFromProto(input).Status - assert.Equal(t, v1.EndpointFailureDomainUnspecified, status.EndpointStatuses[0].LastFailure.Domain) - assert.Nil(t, status.LastTokenGrantFailure.Failure) - input.Status.LastTokenGrantFailure = nil - assert.Nil(t, SandboxFromProto(input).Status.LastTokenGrantFailure) -} - -func TestEndpointResultFromProto(t *testing.T) { - for _, tc := range []struct { - input pb.EndpointResult - want v1.EndpointResult - }{ - {pb.EndpointResult_ENDPOINT_RESULT_UNSPECIFIED, v1.EndpointUnspecified}, - {pb.EndpointResult_ENDPOINT_RESULT_NO_OBSERVED_EXCHANGE, v1.EndpointNoObservedExchange}, - {pb.EndpointResult_ENDPOINT_RESULT_HTTP_RESPONSE_RECEIVED, v1.EndpointHTTPResponseReceived}, - {pb.EndpointResult_ENDPOINT_RESULT_POLICY_DENIED, v1.EndpointPolicyDenied}, - {pb.EndpointResult_ENDPOINT_RESULT_CREDENTIAL_UNAVAILABLE, v1.EndpointCredentialUnavailable}, - {pb.EndpointResult_ENDPOINT_RESULT_TLS_FAILED, v1.EndpointTLSFailed}, - {pb.EndpointResult_ENDPOINT_RESULT_TRANSPORT_FAILED, v1.EndpointTransportFailed}, - {pb.EndpointResult_ENDPOINT_RESULT_UPSTREAM_REJECTED, v1.EndpointUpstreamRejected}, - {pb.EndpointResult(99), v1.EndpointUnspecified}, - } { - t.Run(tc.input.String(), func(t *testing.T) { - assert.Equal(t, tc.want, endpointResultFromProto(tc.input)) - }) - } -} - -func TestSandboxFromProto_NilFields(t *testing.T) { - proto := &pb.Sandbox{} - - s := SandboxFromProto(proto) - - require.NotNil(t, s) - assert.Empty(t, s.ID) - assert.Empty(t, s.Name) - assert.True(t, s.CreatedAt.IsZero()) - assert.Nil(t, s.Spec.Template) - assert.False(t, s.Spec.GPU) - assert.Nil(t, s.Spec.GPUCount) - assert.Equal(t, v1.SandboxUnknown, s.Status.Phase) -} - -func TestSandboxFromProto_DefaultGPURequest(t *testing.T) { - proto := &pb.Sandbox{ - Spec: &pb.SandboxSpec{ - ResourceRequirements: &pb.ResourceRequirements{ - Gpu: &pb.GpuResourceRequirements{}, - }, - }, - } - - s := SandboxFromProto(proto) - - require.NotNil(t, s) - assert.True(t, s.Spec.GPU) - assert.Nil(t, s.Spec.GPUCount) -} - -func TestSandboxFromProto_Nil(t *testing.T) { - s := SandboxFromProto(nil) - assert.Nil(t, s) -} - -func TestSandboxPhaseFromProto(t *testing.T) { - tests := []struct { - proto pb.SandboxPhase - expected v1.SandboxPhase - }{ - {pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING, v1.SandboxProvisioning}, - {pb.SandboxPhase_SANDBOX_PHASE_READY, v1.SandboxReady}, - {pb.SandboxPhase_SANDBOX_PHASE_ERROR, v1.SandboxError}, - {pb.SandboxPhase_SANDBOX_PHASE_DELETING, v1.SandboxDeleting}, - {pb.SandboxPhase_SANDBOX_PHASE_UNKNOWN, v1.SandboxUnknown}, - {pb.SandboxPhase_SANDBOX_PHASE_STOPPING, v1.SandboxStopping}, - {pb.SandboxPhase_SANDBOX_PHASE_STOPPED, v1.SandboxStopped}, - {pb.SandboxPhase_SANDBOX_PHASE_COMPLETED, v1.SandboxCompleted}, - {pb.SandboxPhase_SANDBOX_PHASE_STARTING, v1.SandboxStarting}, - {pb.SandboxPhase_SANDBOX_PHASE_SUSPENDING, v1.SandboxSuspending}, - {pb.SandboxPhase_SANDBOX_PHASE_SUSPENDED, v1.SandboxSuspended}, - {pb.SandboxPhase_SANDBOX_PHASE_UNSPECIFIED, v1.SandboxUnknown}, - {pb.SandboxPhase(999), v1.SandboxUnknown}, - } - - for _, tt := range tests { - assert.Equal(t, tt.expected, SandboxPhaseFromProto(tt.proto), "phase %v", tt.proto) - } -} - -func TestSandboxPhaseToProto(t *testing.T) { - tests := []struct { - sdk v1.SandboxPhase - expected pb.SandboxPhase - }{ - {v1.SandboxProvisioning, pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - {v1.SandboxReady, pb.SandboxPhase_SANDBOX_PHASE_READY}, - {v1.SandboxError, pb.SandboxPhase_SANDBOX_PHASE_ERROR}, - {v1.SandboxDeleting, pb.SandboxPhase_SANDBOX_PHASE_DELETING}, - {v1.SandboxUnknown, pb.SandboxPhase_SANDBOX_PHASE_UNKNOWN}, - {v1.SandboxStopping, pb.SandboxPhase_SANDBOX_PHASE_STOPPING}, - {v1.SandboxStopped, pb.SandboxPhase_SANDBOX_PHASE_STOPPED}, - {v1.SandboxCompleted, pb.SandboxPhase_SANDBOX_PHASE_COMPLETED}, - {v1.SandboxStarting, pb.SandboxPhase_SANDBOX_PHASE_STARTING}, - {v1.SandboxSuspending, pb.SandboxPhase_SANDBOX_PHASE_SUSPENDING}, - {v1.SandboxSuspended, pb.SandboxPhase_SANDBOX_PHASE_SUSPENDED}, - {v1.SandboxPhase("bogus"), pb.SandboxPhase_SANDBOX_PHASE_UNKNOWN}, - } - - for _, tt := range tests { - assert.Equal(t, tt.expected, SandboxPhaseToProto(tt.sdk), "phase %v", tt.sdk) - } -} - -func TestSandboxToProto(t *testing.T) { - userNS := true - gpuCount := uint32(4) - delTime := time.UnixMilli(1700000060000).UTC() - s := &v1.Sandbox{ - ID: "sb-1", - Name: "my-sandbox", - CreatedAt: time.UnixMilli(1700000000000).UTC(), - Labels: map[string]string{"env": "dev"}, - Annotations: map[string]string{"owner": "team-a"}, - ResourceVersion: 3, - Workspace: "prod", - DeletionTimestamp: &delTime, - Spec: v1.SandboxSpec{ - LogLevel: "info", - Environment: map[string]string{"KEY": "val"}, - Template: &v1.SandboxTemplate{ - Image: "img:v1", - RuntimeClassName: "runc", - AgentSocket: "/sock", - Labels: map[string]string{"l": "v"}, - Annotations: map[string]string{"a": "v"}, - Environment: map[string]string{"E": "V"}, - UserNamespaces: &userNS, - }, - Providers: []string{"prov-a"}, - GPUCount: &gpuCount, - Command: []string{"/opt/agent", "--serve"}, - TTY: false, - }, - } - - p := SandboxToProto(s) - - require.NotNil(t, p) - require.NotNil(t, p.Metadata) - assert.Equal(t, "sb-1", p.Metadata.Id) - assert.Equal(t, "my-sandbox", p.Metadata.Name) - assert.Equal(t, int64(1700000000000), MillisFromProto(p.Metadata.CreatedTime)) - assert.Equal(t, map[string]string{"env": "dev"}, p.Metadata.Labels) - assert.Equal(t, map[string]string{"owner": "team-a"}, p.Metadata.Annotations) - assert.Equal(t, uint64(3), p.Metadata.ResourceVersion) - assert.Equal(t, "prod", p.Metadata.Workspace) - assert.Equal(t, int64(1700000060000), MillisFromProto(p.Metadata.DeletionTime)) - - require.NotNil(t, p.Spec) - assert.Equal(t, "info", p.Spec.LogLevel) - assert.Equal(t, map[string]string{"KEY": "val"}, p.Spec.Environment) - assert.Equal(t, []string{"prov-a"}, p.Spec.Providers) - assert.Equal(t, []string{"/opt/agent", "--serve"}, p.Spec.Command) - assert.False(t, p.Spec.Tty) - - require.NotNil(t, p.Spec.ResourceRequirements) - require.NotNil(t, p.Spec.ResourceRequirements.Gpu) - assert.Equal(t, uint32(4), p.Spec.ResourceRequirements.Gpu.GetCount()) - - require.NotNil(t, p.Spec.Template) - assert.Equal(t, "img:v1", p.Spec.Template.Image) - assert.Equal(t, "runc", p.Spec.Template.RuntimeClassName) - assert.Equal(t, "/sock", p.Spec.Template.AgentSocket) - assert.Equal(t, map[string]string{"l": "v"}, p.Spec.Template.Labels) - assert.Equal(t, map[string]string{"a": "v"}, p.Spec.Template.Annotations) - assert.Equal(t, map[string]string{"E": "V"}, p.Spec.Template.Environment) - require.NotNil(t, p.Spec.Template.UserNamespaces) - assert.True(t, *p.Spec.Template.UserNamespaces) -} - -func TestSandboxToProto_Nil(t *testing.T) { - p := SandboxToProto(nil) - assert.Nil(t, p) -} - -func TestSandboxToProto_NilTemplate(t *testing.T) { - s := &v1.Sandbox{ - Spec: v1.SandboxSpec{ - LogLevel: "warn", - }, - } - - p := SandboxToProto(s) - - require.NotNil(t, p) - require.NotNil(t, p.Spec) - assert.Nil(t, p.Spec.Template) - assert.Nil(t, p.Spec.ResourceRequirements) -} - -func TestSandboxToProto_DefaultGPURequest(t *testing.T) { - s := &v1.Sandbox{ - Spec: v1.SandboxSpec{ - GPU: true, - }, - } - - p := SandboxToProto(s) - - require.NotNil(t, p) - require.NotNil(t, p.Spec) - require.NotNil(t, p.Spec.ResourceRequirements) - require.NotNil(t, p.Spec.ResourceRequirements.Gpu) - assert.Nil(t, p.Spec.ResourceRequirements.Gpu.Count) -} - -func TestSandboxWorkloadTemplateRoundTrip(t *testing.T) { - gpuCount := uint32(2) - delTime := time.UnixMilli(1700000060000).UTC() - original := &v1.SandboxWorkloadTemplate{ - ID: "tmpl-1", - Name: "gpu-kata", - CreatedAt: time.UnixMilli(1700000000000).UTC(), - Labels: map[string]string{"team": "platform"}, - Annotations: map[string]string{"note": "fast-start"}, - ResourceVersion: 9, - Workspace: "prod", - DeletionTimestamp: &delTime, - Spec: v1.SandboxWorkloadTemplateSpec{ - Workload: &v1.SandboxWorkloadConfig{ - Image: "nvcr.io/nvidia/openshell:latest", - Environment: map[string]string{"CUDA_VISIBLE_DEVICES": "all"}, - Resources: &v1.SandboxResources{ - CPU: "2", - Memory: "8Gi", - GPU: &v1.SandboxGPURequirements{Count: &gpuCount}, - }, - }, - DriverConfig: map[string]any{ - "kubernetes": map[string]any{"runtimeClassName": "kata"}, - }, - DesiredServiceLevel: &v1.SandboxServiceLevel{ - Startup: &v1.SandboxStartup{ - ReadyWithin: 30 * time.Second, - MaxBurst: 3, - }, - }, - }, - } - - protoTemplate, err := SandboxWorkloadTemplateToProtoChecked(original) - require.NoError(t, err) - - require.NotNil(t, protoTemplate.Metadata) - assert.Equal(t, "gpu-kata", protoTemplate.Metadata.Name) - require.NotNil(t, protoTemplate.Spec) - require.NotNil(t, protoTemplate.Spec.Workload) - assert.Equal(t, "nvcr.io/nvidia/openshell:latest", protoTemplate.Spec.Workload.Image) - assert.Equal(t, map[string]string{"CUDA_VISIBLE_DEVICES": "all"}, protoTemplate.Spec.Workload.Environment) - require.NotNil(t, protoTemplate.Spec.Workload.Resources) - assert.Equal(t, "2", protoTemplate.Spec.Workload.Resources.Cpu) - assert.Equal(t, "8Gi", protoTemplate.Spec.Workload.Resources.Memory) - require.NotNil(t, protoTemplate.Spec.Workload.Resources.Gpu) - require.NotNil(t, protoTemplate.Spec.Workload.Resources.Gpu.Count) - assert.Equal(t, uint32(2), *protoTemplate.Spec.Workload.Resources.Gpu.Count) - require.NotNil(t, protoTemplate.Spec.DriverConfig) - require.NotNil(t, protoTemplate.Spec.DesiredServiceLevel) - require.NotNil(t, protoTemplate.Spec.DesiredServiceLevel.Startup) - assert.Equal(t, durationpb.New(30*time.Second), protoTemplate.Spec.DesiredServiceLevel.Startup.ReadyWithin) - assert.Equal(t, uint32(3), protoTemplate.Spec.DesiredServiceLevel.Startup.MaxBurst) - - back := SandboxWorkloadTemplateFromProto(protoTemplate) - require.NotNil(t, back) - assert.Equal(t, original.ID, back.ID) - assert.Equal(t, original.Name, back.Name) - assert.Equal(t, original.CreatedAt, back.CreatedAt) - assert.Equal(t, original.Labels, back.Labels) - assert.Equal(t, original.Annotations, back.Annotations) - assert.Equal(t, original.ResourceVersion, back.ResourceVersion) - assert.Equal(t, original.Workspace, back.Workspace) - require.NotNil(t, back.DeletionTimestamp) - assert.Equal(t, *original.DeletionTimestamp, *back.DeletionTimestamp) - require.NotNil(t, back.Spec.Workload) - assert.Equal(t, original.Spec.Workload.Image, back.Spec.Workload.Image) - assert.Equal(t, original.Spec.Workload.Environment, back.Spec.Workload.Environment) - require.NotNil(t, back.Spec.Workload.Resources) - assert.Equal(t, original.Spec.Workload.Resources.CPU, back.Spec.Workload.Resources.CPU) - assert.Equal(t, original.Spec.Workload.Resources.Memory, back.Spec.Workload.Resources.Memory) - require.NotNil(t, back.Spec.Workload.Resources.GPU) - require.NotNil(t, back.Spec.Workload.Resources.GPU.Count) - assert.Equal(t, *original.Spec.Workload.Resources.GPU.Count, *back.Spec.Workload.Resources.GPU.Count) - assert.Equal(t, "kata", back.Spec.DriverConfig["kubernetes"].(map[string]any)["runtimeClassName"]) - require.NotNil(t, back.Spec.DesiredServiceLevel) - require.NotNil(t, back.Spec.DesiredServiceLevel.Startup) - assert.Equal(t, 30*time.Second, back.Spec.DesiredServiceLevel.Startup.ReadyWithin) - assert.Equal(t, uint32(3), back.Spec.DesiredServiceLevel.Startup.MaxBurst) -} - -func TestSandboxWorkloadTemplateRoundTrip_DefaultGpuRequest(t *testing.T) { - original := &v1.SandboxWorkloadTemplate{ - Name: "default-gpu", - Spec: v1.SandboxWorkloadTemplateSpec{ - Workload: &v1.SandboxWorkloadConfig{ - Resources: &v1.SandboxResources{ - GPU: &v1.SandboxGPURequirements{}, - }, - }, - }, - } - - protoTemplate, err := SandboxWorkloadTemplateToProtoChecked(original) - require.NoError(t, err) - require.NotNil(t, protoTemplate.GetSpec().GetWorkload().GetResources().GetGpu()) - assert.Nil(t, protoTemplate.GetSpec().GetWorkload().GetResources().GetGpu().Count) - - back := SandboxWorkloadTemplateFromProto(protoTemplate) - require.NotNil(t, back.Spec.Workload.Resources.GPU) - assert.Nil(t, back.Spec.Workload.Resources.GPU.Count) -} - -func TestSandboxWorkloadTemplateToProtoChecked_RejectsUnrepresentableDriverConfig(t *testing.T) { - _, err := SandboxWorkloadTemplateToProtoChecked(&v1.SandboxWorkloadTemplate{ - Name: "bad", - Spec: v1.SandboxWorkloadTemplateSpec{ - DriverConfig: map[string]any{"invalid": make(chan int)}, - }, - }) - require.Error(t, err) - assert.Contains(t, err.Error(), "driver config") -} - -func TestSandboxRoundTrip(t *testing.T) { - userNS := false - gpuCount := uint32(1) - rtDelTime := time.UnixMilli(1700000090000).UTC() - original := &v1.Sandbox{ - ID: "sb-rt", - Name: "round-trip", - CreatedAt: time.UnixMilli(1700000000000).UTC(), - Labels: map[string]string{"team": "platform"}, - Annotations: map[string]string{"note": "rt-test"}, - ResourceVersion: 10, - Workspace: "staging", - DeletionTimestamp: &rtDelTime, - Spec: v1.SandboxSpec{ - LogLevel: "trace", - Environment: map[string]string{"A": "B"}, - Template: &v1.SandboxTemplate{ - Image: "img:rt", - UserNamespaces: &userNS, - }, - Providers: []string{"p1", "p2"}, - GPUCount: &gpuCount, - Policy: &v1.SandboxPolicy{ - Version: 3, - Filesystem: &v1.FilesystemPolicy{ - IncludeWorkdir: true, - ReadOnly: []string{"/etc", "/usr/share"}, - ReadWrite: []string{"/tmp"}, - }, - Landlock: &v1.LandlockPolicy{ - Compatibility: "best_effort", - }, - Process: &v1.ProcessPolicy{ - RunAsUser: "sandbox", - RunAsGroup: "sandbox-group", - }, - NetworkPolicies: map[string]v1.NetworkPolicyRule{ - "web": { - Name: "web", - Endpoints: []v1.PolicyNetworkEndpoint{ - { - Host: "api.example.com", - Port: 443, - Protocol: "rest", - CredentialBinding: &v1.NetworkCredentialBinding{ - Provider: "api-credentials", - }, - }, - }, - }, - }, - }, - }, - } - - p := SandboxToProto(original) - back := SandboxFromProto(p) - - assert.Equal(t, original.ID, back.ID) - assert.Equal(t, original.Name, back.Name) - assert.Equal(t, original.CreatedAt, back.CreatedAt) - assert.Equal(t, original.Labels, back.Labels) - assert.Equal(t, original.Annotations, back.Annotations) - assert.Equal(t, original.ResourceVersion, back.ResourceVersion) - assert.Equal(t, original.Workspace, back.Workspace) - require.NotNil(t, back.DeletionTimestamp) - assert.Equal(t, *original.DeletionTimestamp, *back.DeletionTimestamp) - assert.Equal(t, original.Spec.LogLevel, back.Spec.LogLevel) - assert.Equal(t, original.Spec.Environment, back.Spec.Environment) - assert.Equal(t, original.Spec.Providers, back.Spec.Providers) - assert.True(t, back.Spec.GPU) - require.NotNil(t, back.Spec.GPUCount) - assert.Equal(t, *original.Spec.GPUCount, *back.Spec.GPUCount) - require.NotNil(t, back.Spec.Template) - assert.Equal(t, original.Spec.Template.Image, back.Spec.Template.Image) - require.NotNil(t, back.Spec.Template.UserNamespaces) - assert.Equal(t, *original.Spec.Template.UserNamespaces, *back.Spec.Template.UserNamespaces) - - // Policy round-trip - require.NotNil(t, back.Spec.Policy) - assert.Equal(t, uint32(3), back.Spec.Policy.Version) - require.NotNil(t, back.Spec.Policy.Filesystem) - assert.True(t, back.Spec.Policy.Filesystem.IncludeWorkdir) - assert.Equal(t, []string{"/etc", "/usr/share"}, back.Spec.Policy.Filesystem.ReadOnly) - assert.Equal(t, []string{"/tmp"}, back.Spec.Policy.Filesystem.ReadWrite) - require.NotNil(t, back.Spec.Policy.Landlock) - assert.Equal(t, "best_effort", back.Spec.Policy.Landlock.Compatibility) - require.NotNil(t, back.Spec.Policy.Process) - assert.Equal(t, "sandbox", back.Spec.Policy.Process.RunAsUser) - assert.Equal(t, "sandbox-group", back.Spec.Policy.Process.RunAsGroup) - require.Len(t, back.Spec.Policy.NetworkPolicies, 1) - webRule, ok := back.Spec.Policy.NetworkPolicies["web"] - require.True(t, ok) - assert.Equal(t, "web", webRule.Name) - require.Len(t, webRule.Endpoints, 1) - assert.Equal(t, "api.example.com", webRule.Endpoints[0].Host) - require.NotNil(t, webRule.Endpoints[0].CredentialBinding) - assert.Equal(t, "api-credentials", webRule.Endpoints[0].CredentialBinding.Provider) -} - -func TestMcpOptionsConversionCopiesVersions(t *testing.T) { - wire := &sandboxpb.McpOptions{ - Versions: []string{"2025-03-26", "2025-11-25"}, - } - - sdk := mcpOptionsFromProto(wire) - require.NotNil(t, sdk) - assert.Equal(t, []string{"2025-03-26", "2025-11-25"}, sdk.Versions) - wire.Versions[0] = "mutated-wire-value" - assert.Equal(t, "2025-03-26", sdk.Versions[0]) - - roundTrip := mcpOptionsToProto(sdk) - require.NotNil(t, roundTrip) - assert.Equal(t, []string{"2025-03-26", "2025-11-25"}, roundTrip.Versions) - sdk.Versions[0] = "mutated-sdk-value" - assert.Equal(t, "2025-03-26", roundTrip.Versions[0]) -} - -func TestMcpOptionsConversionDoesNotMaterializeDefaultVersions(t *testing.T) { - wire := &sandboxpb.McpOptions{Versions: []string{}} - - sdk := mcpOptionsFromProto(wire) - require.NotNil(t, sdk) - assert.NotNil(t, sdk.Versions) - assert.Empty(t, sdk.Versions) - - roundTrip := mcpOptionsToProto(sdk) - require.NotNil(t, roundTrip) - assert.NotNil(t, roundTrip.Versions) - assert.Empty(t, roundTrip.Versions) -} - -func TestSandboxSpecToProto(t *testing.T) { - gpuCount := uint32(3) - spec := &v1.SandboxSpec{ - LogLevel: "debug", - Environment: map[string]string{"X": "Y"}, - Template: &v1.SandboxTemplate{ - Image: "img:spec", - Resources: map[string]any{"cpu": "4"}, - DriverConfig: map[string]any{"runtime": "kata"}, - }, - Providers: []string{"prov"}, - GPUCount: &gpuCount, - Policy: &v1.SandboxPolicy{ - Version: 2, - Filesystem: &v1.FilesystemPolicy{ - ReadOnly: []string{"/etc"}, - }, - }, - } - - p := SandboxSpecToProto(spec) - - require.NotNil(t, p) - assert.Equal(t, "debug", p.LogLevel) - assert.Equal(t, map[string]string{"X": "Y"}, p.Environment) - assert.Equal(t, []string{"prov"}, p.Providers) - require.NotNil(t, p.ResourceRequirements) - assert.Equal(t, uint32(3), p.ResourceRequirements.Gpu.GetCount()) - require.NotNil(t, p.Template) - assert.Equal(t, "img:spec", p.Template.Image) - require.NotNil(t, p.Template.Resources) - assert.Equal(t, "4", p.Template.Resources.Fields["cpu"].GetStringValue()) - require.NotNil(t, p.Template.DriverConfig) - assert.Equal(t, "kata", p.Template.DriverConfig.Fields["runtime"].GetStringValue()) - - // Policy conversion - require.NotNil(t, p.Policy) - assert.Equal(t, uint32(2), p.Policy.Version) - require.NotNil(t, p.Policy.Filesystem) - assert.Equal(t, []string{"/etc"}, p.Policy.Filesystem.ReadOnly) -} - -func TestSandboxSpecToProto_Nil(t *testing.T) { - p := SandboxSpecToProto(nil) - assert.Nil(t, p) -} - -// Verify proto import is used (suppress unused import warning). -var _ = proto.Marshal diff --git a/sdk/go/openshell/v1/internal/converter/service.go b/sdk/go/openshell/v1/internal/converter/service.go deleted file mode 100644 index bb2803c6bb..0000000000 --- a/sdk/go/openshell/v1/internal/converter/service.go +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" -) - -// ServiceEndpointFromProto converts a proto ServiceEndpointResponse to an SDK ServiceEndpoint. -// The response flattens the nested Endpoint and top-level URL into a single SDK type. -func ServiceEndpointFromProto(resp *pb.ServiceEndpointResponse) *types.ServiceEndpoint { - if resp == nil { - return nil - } - - result := &types.ServiceEndpoint{ - URL: resp.GetUrl(), - } - - if ep := resp.GetEndpoint(); ep != nil { - result.SandboxID = ep.GetSandboxId() - result.Sandbox = ep.GetSandbox() - result.Name = ep.GetName() - result.TargetPort = ep.GetTargetPort() - result.Domain = ep.GetDomain() - result.AuthorizationMode = serviceAuthorizationModeFromProto(ep.GetAuthorizationMode()) - - if m := ep.GetMetadata(); m != nil { - result.ID = m.GetId() - result.Workspace = m.GetWorkspace() - } - } - - return result -} - -// ServiceEndpointToProto converts an SDK ServiceEndpoint to a proto ServiceEndpointResponse. -func ServiceEndpointToProto(se *types.ServiceEndpoint) *pb.ServiceEndpointResponse { - if se == nil { - return nil - } - - return &pb.ServiceEndpointResponse{ - Endpoint: &pb.ServiceEndpoint{ - Metadata: &dm.ObjectMeta{ - Id: se.ID, - Workspace: se.Workspace, - }, - SandboxId: se.SandboxID, - Sandbox: se.Sandbox, - Name: se.Name, - TargetPort: se.TargetPort, - Domain: se.Domain, - AuthorizationMode: serviceAuthorizationModeToProto(se.AuthorizationMode), - }, - Url: se.URL, - } -} - -func serviceAuthorizationModeToProto(mode types.ServiceAuthorizationMode) pb.ServiceAuthorizationMode { - if mode == types.ServiceAuthorizationModeBearerPassthrough { - return pb.ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_BEARER_PASSTHROUGH - } - return pb.ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_STRIP -} - -func serviceAuthorizationModeFromProto(mode pb.ServiceAuthorizationMode) types.ServiceAuthorizationMode { - if mode == pb.ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_BEARER_PASSTHROUGH { - return types.ServiceAuthorizationModeBearerPassthrough - } - return types.ServiceAuthorizationModeStrip -} diff --git a/sdk/go/openshell/v1/internal/converter/service_test.go b/sdk/go/openshell/v1/internal/converter/service_test.go deleted file mode 100644 index 6db09701e3..0000000000 --- a/sdk/go/openshell/v1/internal/converter/service_test.go +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestServiceEndpointFromProto(t *testing.T) { - resp := &pb.ServiceEndpointResponse{ - Endpoint: &pb.ServiceEndpoint{ - Metadata: &dm.ObjectMeta{ - Id: "svc-1", - }, - SandboxId: "sb-1", - Sandbox: "my-sandbox", - Name: "http-server", - TargetPort: 8080, - Domain: true, - AuthorizationMode: pb.ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_BEARER_PASSTHROUGH, - }, - Url: "https://svc-1.example.com", - } - - se := ServiceEndpointFromProto(resp) - - require.NotNil(t, se) - assert.Equal(t, "svc-1", se.ID) - assert.Equal(t, "sb-1", se.SandboxID) - assert.Equal(t, "my-sandbox", se.Sandbox) - assert.Equal(t, "http-server", se.Name) - assert.Equal(t, uint32(8080), se.TargetPort) - assert.True(t, se.Domain) - assert.Equal(t, v1.ServiceAuthorizationModeBearerPassthrough, se.AuthorizationMode) - assert.Equal(t, "https://svc-1.example.com", se.URL) -} - -func TestServiceEndpointFromProto_NilEndpoint(t *testing.T) { - resp := &pb.ServiceEndpointResponse{ - Url: "https://orphan.example.com", - } - - se := ServiceEndpointFromProto(resp) - - require.NotNil(t, se) - assert.Empty(t, se.ID) - assert.Empty(t, se.SandboxID) - assert.Equal(t, "https://orphan.example.com", se.URL) -} - -func TestServiceEndpointFromProto_NilMetadata(t *testing.T) { - resp := &pb.ServiceEndpointResponse{ - Endpoint: &pb.ServiceEndpoint{ - SandboxId: "sb-2", - Name: "api", - TargetPort: 3000, - }, - } - - se := ServiceEndpointFromProto(resp) - - require.NotNil(t, se) - assert.Empty(t, se.ID) - assert.Equal(t, "sb-2", se.SandboxID) - assert.Equal(t, "api", se.Name) - assert.Equal(t, uint32(3000), se.TargetPort) -} - -func TestServiceEndpointFromProto_Nil(t *testing.T) { - se := ServiceEndpointFromProto(nil) - assert.Nil(t, se) -} - -func TestServiceEndpointToProto(t *testing.T) { - se := &v1.ServiceEndpoint{ - ID: "svc-1", - SandboxID: "sb-1", - Sandbox: "my-sandbox", - Name: "http-server", - TargetPort: 8080, - Domain: true, - AuthorizationMode: v1.ServiceAuthorizationModeBearerPassthrough, - URL: "https://svc-1.example.com", - } - - resp := ServiceEndpointToProto(se) - - require.NotNil(t, resp) - require.NotNil(t, resp.Endpoint) - require.NotNil(t, resp.Endpoint.Metadata) - assert.Equal(t, "svc-1", resp.Endpoint.Metadata.Id) - assert.Equal(t, "sb-1", resp.Endpoint.SandboxId) - assert.Equal(t, "my-sandbox", resp.Endpoint.Sandbox) - assert.Equal(t, "http-server", resp.Endpoint.Name) - assert.Equal(t, uint32(8080), resp.Endpoint.TargetPort) - assert.True(t, resp.Endpoint.Domain) - assert.Equal(t, pb.ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_BEARER_PASSTHROUGH, resp.Endpoint.AuthorizationMode) - assert.Equal(t, "https://svc-1.example.com", resp.Url) -} - -func TestServiceEndpointToProto_Nil(t *testing.T) { - resp := ServiceEndpointToProto(nil) - assert.Nil(t, resp) -} - -func TestServiceEndpointRoundTrip(t *testing.T) { - original := &v1.ServiceEndpoint{ - ID: "svc-rt", - SandboxID: "sb-rt", - Sandbox: "round-trip", - Name: "web", - TargetPort: 9090, - Domain: false, - AuthorizationMode: v1.ServiceAuthorizationModeBearerPassthrough, - URL: "http://localhost:9090", - } - - proto := ServiceEndpointToProto(original) - back := ServiceEndpointFromProto(proto) - - require.NotNil(t, back) - assert.Equal(t, original.ID, back.ID) - assert.Equal(t, original.SandboxID, back.SandboxID) - assert.Equal(t, original.Sandbox, back.Sandbox) - assert.Equal(t, original.Name, back.Name) - assert.Equal(t, original.TargetPort, back.TargetPort) - assert.Equal(t, original.Domain, back.Domain) - assert.Equal(t, original.AuthorizationMode, back.AuthorizationMode) - assert.Equal(t, original.URL, back.URL) -} diff --git a/sdk/go/openshell/v1/internal/converter/setting.go b/sdk/go/openshell/v1/internal/converter/setting.go deleted file mode 100644 index 57a0ae1242..0000000000 --- a/sdk/go/openshell/v1/internal/converter/setting.go +++ /dev/null @@ -1,334 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "fmt" - "slices" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - sbv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" -) - -// --- SettingValue oneof conversion --- - -// SettingValueFromProto converts a proto SettingValue (oneof) to an SDK SettingValue. -func SettingValueFromProto(pv *sbv1.SettingValue) *v1.SettingValue { - if pv == nil { - return nil - } - sv := &v1.SettingValue{} - switch v := pv.GetValue().(type) { - case *sbv1.SettingValue_StringValue: - sv.Type = v1.SettingValueString - sv.StringVal = v.StringValue - case *sbv1.SettingValue_BoolValue: - sv.Type = v1.SettingValueBool - sv.BoolVal = v.BoolValue - case *sbv1.SettingValue_IntValue: - sv.Type = v1.SettingValueInt - sv.IntVal = v.IntValue - case *sbv1.SettingValue_BytesValue: - sv.Type = v1.SettingValueBytes - sv.BytesVal = CopyByteSlice(v.BytesValue) - } - return sv -} - -// SettingValueToProto converts an SDK SettingValue to a proto SettingValue (oneof). -func SettingValueToProto(sv *v1.SettingValue) *sbv1.SettingValue { - if sv == nil { - return nil - } - pv := &sbv1.SettingValue{} - switch sv.Type { - case v1.SettingValueString: - pv.Value = &sbv1.SettingValue_StringValue{StringValue: sv.StringVal} - case v1.SettingValueBool: - pv.Value = &sbv1.SettingValue_BoolValue{BoolValue: sv.BoolVal} - case v1.SettingValueInt: - pv.Value = &sbv1.SettingValue_IntValue{IntValue: sv.IntVal} - case v1.SettingValueBytes: - pv.Value = &sbv1.SettingValue_BytesValue{BytesValue: CopyByteSlice(sv.BytesVal)} - } - return pv -} - -// --- Enum conversions --- - -// SettingScopeFromProto converts a proto SettingScope enum to an SDK SettingScope. -func SettingScopeFromProto(ps sbv1.SettingScope) v1.SettingScope { - switch ps { - case sbv1.SettingScope_SETTING_SCOPE_UNSPECIFIED: - return v1.SettingScopeUnspecified - case sbv1.SettingScope_SETTING_SCOPE_SANDBOX: - return v1.SettingScopeSandbox - case sbv1.SettingScope_SETTING_SCOPE_GLOBAL: - return v1.SettingScopeGlobal - default: - return v1.SettingScope("") - } -} - -// SettingScopeToProto converts an SDK SettingScope to a proto SettingScope enum. -func SettingScopeToProto(s v1.SettingScope) sbv1.SettingScope { - switch s { - case v1.SettingScopeUnspecified: - return sbv1.SettingScope_SETTING_SCOPE_UNSPECIFIED - case v1.SettingScopeSandbox: - return sbv1.SettingScope_SETTING_SCOPE_SANDBOX - case v1.SettingScopeGlobal: - return sbv1.SettingScope_SETTING_SCOPE_GLOBAL - default: - return sbv1.SettingScope_SETTING_SCOPE_UNSPECIFIED - } -} - -// PolicySourceFromProto converts a proto PolicySource enum to an SDK PolicySource. -func PolicySourceFromProto(ps sbv1.PolicySource) v1.PolicySource { - switch ps { - case sbv1.PolicySource_POLICY_SOURCE_UNSPECIFIED: - return v1.PolicySourceUnspecified - case sbv1.PolicySource_POLICY_SOURCE_SANDBOX: - return v1.PolicySourceSandbox - case sbv1.PolicySource_POLICY_SOURCE_GLOBAL: - return v1.PolicySourceGlobal - default: - return v1.PolicySource("") - } -} - -// --- EffectiveSetting --- - -// EffectiveSettingFromProto converts a proto EffectiveSetting to an SDK EffectiveSetting. -func EffectiveSettingFromProto(pv *sbv1.EffectiveSetting) *v1.EffectiveSetting { - if pv == nil { - return nil - } - es := &v1.EffectiveSetting{ - Scope: SettingScopeFromProto(pv.GetScope()), - } - if sv := SettingValueFromProto(pv.GetValue()); sv != nil { - es.Value = *sv - } - return es -} - -// --- SandboxConfig --- - -// SandboxConfigFromProto converts a GetSandboxConfigResponse to an SDK SandboxConfig. -func SandboxConfigFromProto(resp *sbv1.GetSandboxConfigResponse) *v1.SandboxConfig { - if resp == nil { - return nil - } - sc := &v1.SandboxConfig{ - PolicyVersion: resp.GetVersion(), - PolicyHash: resp.GetPolicyHash(), - ConfigRevision: resp.GetConfigRevision(), - PolicySource: PolicySourceFromProto(resp.GetPolicySource()), - GlobalPolicyVersion: resp.GetGlobalPolicyVersion(), - ProviderEnvRevision: resp.GetProviderEnvRevision(), - PolicyValidationFailureMode: resp.GetPolicyValidationFailureMode(), - } - - // Convert proto SandboxPolicy to typed SDK SandboxPolicy. - sc.Policy = SandboxPolicyFromProto(resp.GetPolicy()) - - // Deep-copy settings map. - if m := resp.GetSettings(); len(m) > 0 { - sc.Settings = make(map[string]v1.EffectiveSetting, len(m)) - for k, v := range m { - if es := EffectiveSettingFromProto(v); es != nil { - sc.Settings[k] = *es - } - } - } - - return sc -} - -// --- GatewayConfig --- - -// GatewayConfigFromProto converts a GetGatewayConfigResponse to an SDK GatewayConfig. -func GatewayConfigFromProto(resp *sbv1.GetGatewayConfigResponse) *v1.GatewayConfig { - if resp == nil { - return nil - } - gc := &v1.GatewayConfig{ - SettingsRevision: resp.GetSettingsRevision(), - } - - // Deep-copy settings map. - if m := resp.GetSettings(); len(m) > 0 { - gc.Settings = make(map[string]v1.SettingValue, len(m)) - for k, v := range m { - if sv := SettingValueFromProto(v); sv != nil { - gc.Settings[k] = *sv - } - } - } - - return gc -} - -// --- ConfigUpdate --- - -// ConfigUpdateToProto converts an SDK ConfigUpdate to an UpdateConfigRequest. -func ConfigUpdateToProto(cu *v1.ConfigUpdate) (*pb.UpdateConfigRequest, error) { - if cu == nil { - return nil, nil - } - req := &pb.UpdateConfigRequest{ - SettingKey: cu.SettingKey, - SettingValue: SettingValueToProto(cu.SettingValue), - DeleteSetting: cu.DeleteSetting, - Global: cu.Global, - ExpectedResourceVersion: cu.ExpectedResourceVersion, - Annotations: CopyStringMap(cu.Annotations), - } - if !cu.Global { - req.Sandbox = cu.Name - } - - // Convert typed SDK SandboxPolicy to proto SandboxPolicy. - policy, err := SandboxPolicyToProtoChecked(cu.Policy) - if err != nil { - return nil, err - } - req.Policy = policy - - // Convert typed merge operations with validation. - if len(cu.MergeOperations) > 0 { - req.MergeOperations = make([]*pb.PolicyMergeOperation, len(cu.MergeOperations)) - for i := range cu.MergeOperations { - converted, err := PolicyMergeOperationToProto(&cu.MergeOperations[i]) - if err != nil { - return nil, fmt.Errorf("merge operation [%d]: %w", i, err) - } - req.MergeOperations[i] = converted - } - } - - return req, nil -} - -// --- PolicyMergeOperation --- - -// PolicyMergeOperationToProto converts an SDK PolicyMergeOperation to a proto PolicyMergeOperation. -// Exactly one of the pointer fields must be non-nil. Returns an error if zero or multiple are set. -func PolicyMergeOperationToProto(op *v1.PolicyMergeOperation) (*pb.PolicyMergeOperation, error) { - if op == nil { - return nil, nil - } - set := boolCount(op.AddRule != nil, op.RemoveEndpoint != nil, op.RemoveRule != nil, - op.AddDenyRules != nil, op.AddAllowRules != nil, op.RemoveBinary != nil) - if set != 1 { - return nil, fmt.Errorf("PolicyMergeOperation: exactly one variant must be set, got %d", set) - } - pmo := &pb.PolicyMergeOperation{} - switch { - case op.AddRule != nil: - rule := NetworkPolicyRuleToProto(&op.AddRule.Rule) - pmo.Operation = &pb.PolicyMergeOperation_AddRule{ - AddRule: &pb.AddNetworkRule{ - RuleName: op.AddRule.RuleName, - Rule: rule, - }, - } - case op.RemoveEndpoint != nil: - pmo.Operation = &pb.PolicyMergeOperation_RemoveEndpoint{ - RemoveEndpoint: &pb.RemoveNetworkEndpoint{ - RuleName: op.RemoveEndpoint.RuleName, - Host: op.RemoveEndpoint.Host, - Port: op.RemoveEndpoint.Port, - }, - } - case op.RemoveRule != nil: - pmo.Operation = &pb.PolicyMergeOperation_RemoveRule{ - RemoveRule: &pb.RemoveNetworkRule{ - RuleName: op.RemoveRule.RuleName, - }, - } - case op.AddDenyRules != nil: - var denyRules []*sbv1.L7DenyRule - if len(op.AddDenyRules.DenyRules) > 0 { - denyRules = make([]*sbv1.L7DenyRule, len(op.AddDenyRules.DenyRules)) - for i := range op.AddDenyRules.DenyRules { - denyRules[i] = l7DenyRuleToProto(&op.AddDenyRules.DenyRules[i]) - } - } - pmo.Operation = &pb.PolicyMergeOperation_AddDenyRules{ - AddDenyRules: &pb.AddDenyRules{ - Target: l7RuleTargetToProto(op.AddDenyRules.Target), - DenyRules: denyRules, - }, - } - case op.AddAllowRules != nil: - var rules []*sbv1.L7Rule - if len(op.AddAllowRules.Rules) > 0 { - rules = make([]*sbv1.L7Rule, len(op.AddAllowRules.Rules)) - for i := range op.AddAllowRules.Rules { - rules[i] = l7RuleToProto(&op.AddAllowRules.Rules[i]) - } - } - pmo.Operation = &pb.PolicyMergeOperation_AddAllowRules{ - AddAllowRules: &pb.AddAllowRules{ - Target: l7RuleTargetToProto(op.AddAllowRules.Target), - Rules: rules, - }, - } - case op.RemoveBinary != nil: - pmo.Operation = &pb.PolicyMergeOperation_RemoveBinary{ - RemoveBinary: &pb.RemoveNetworkBinary{ - RuleName: op.RemoveBinary.RuleName, - BinaryPath: op.RemoveBinary.BinaryPath, - }, - } - } - return pmo, nil -} - -// l7RuleTargetToProto preserves the caller's declaration without inferring scope. -// The gateway rejects missing or mismatched scope against the current policy. -func l7RuleTargetToProto(target *v1.L7RuleTarget) *pb.L7RuleTarget { - if target == nil { - return nil - } - result := &pb.L7RuleTarget{ - RuleName: target.RuleName, - Host: target.Host, - Ports: slices.Clone(target.Ports), - AnyBinary: target.AnyBinary, - } - // Copy optional presence as well as value: an empty path selects an - // unscoped endpoint, while nil leaves path disambiguation to the gateway. - if target.Path != nil { - path := *target.Path - result.Path = &path - } - if target.Binaries != nil { - result.Binaries = make([]*sbv1.NetworkBinary, len(target.Binaries)) - for i, binary := range target.Binaries { - result.Binaries[i] = &sbv1.NetworkBinary{Path: binary.Path} - } - } - return result -} - -// --- ConfigUpdateResult --- - -// ConfigUpdateResultFromProto converts an UpdateConfigResponse to an SDK ConfigUpdateResult. -func ConfigUpdateResultFromProto(resp *pb.UpdateConfigResponse) *v1.ConfigUpdateResult { - if resp == nil { - return nil - } - return &v1.ConfigUpdateResult{ - Version: resp.GetVersion(), - PolicyHash: resp.GetPolicyHash(), - SettingsRevision: resp.GetSettingsRevision(), - Deleted: resp.GetDeleted(), - Annotations: CopyStringMap(resp.GetAnnotations()), - } -} diff --git a/sdk/go/openshell/v1/internal/converter/setting_test.go b/sdk/go/openshell/v1/internal/converter/setting_test.go deleted file mode 100644 index 5481235137..0000000000 --- a/sdk/go/openshell/v1/internal/converter/setting_test.go +++ /dev/null @@ -1,936 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - sbv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// --- SettingValue oneof mapping --- - -func TestSettingValueFromProto_StringValue(t *testing.T) { - pv := &sbv1.SettingValue{ - Value: &sbv1.SettingValue_StringValue{StringValue: "hello"}, - } - - sv := SettingValueFromProto(pv) - - require.NotNil(t, sv) - assert.Equal(t, v1.SettingValueString, sv.Type) - assert.Equal(t, "hello", sv.StringVal) - assert.False(t, sv.BoolVal) - assert.Zero(t, sv.IntVal) - assert.Nil(t, sv.BytesVal) -} - -func TestSettingValueFromProto_BoolValue(t *testing.T) { - pv := &sbv1.SettingValue{ - Value: &sbv1.SettingValue_BoolValue{BoolValue: true}, - } - - sv := SettingValueFromProto(pv) - - require.NotNil(t, sv) - assert.Equal(t, v1.SettingValueBool, sv.Type) - assert.True(t, sv.BoolVal) - assert.Empty(t, sv.StringVal) -} - -func TestSettingValueFromProto_IntValue(t *testing.T) { - pv := &sbv1.SettingValue{ - Value: &sbv1.SettingValue_IntValue{IntValue: 42}, - } - - sv := SettingValueFromProto(pv) - - require.NotNil(t, sv) - assert.Equal(t, v1.SettingValueInt, sv.Type) - assert.Equal(t, int64(42), sv.IntVal) -} - -func TestSettingValueFromProto_BytesValue(t *testing.T) { - data := []byte{0xDE, 0xAD, 0xBE, 0xEF} - pv := &sbv1.SettingValue{ - Value: &sbv1.SettingValue_BytesValue{BytesValue: data}, - } - - sv := SettingValueFromProto(pv) - - require.NotNil(t, sv) - assert.Equal(t, v1.SettingValueBytes, sv.Type) - assert.Equal(t, data, sv.BytesVal) -} - -func TestSettingValueFromProto_BytesDeepCopy(t *testing.T) { - data := []byte{0x01, 0x02, 0x03} - pv := &sbv1.SettingValue{ - Value: &sbv1.SettingValue_BytesValue{BytesValue: data}, - } - - sv := SettingValueFromProto(pv) - - require.NotNil(t, sv) - // Mutate original data — SDK copy must not be affected. - data[0] = 0xFF - assert.Equal(t, byte(0x01), sv.BytesVal[0], "deep copy must isolate SDK from proto") -} - -func TestSettingValueFromProto_NilOneof(t *testing.T) { - pv := &sbv1.SettingValue{} - - sv := SettingValueFromProto(pv) - - require.NotNil(t, sv) - assert.Equal(t, v1.SettingValueType(""), sv.Type) -} - -func TestSettingValueFromProto_Nil(t *testing.T) { - sv := SettingValueFromProto(nil) - assert.Nil(t, sv) -} - -func TestSettingValueToProto_StringValue(t *testing.T) { - sv := &v1.SettingValue{ - Type: v1.SettingValueString, - StringVal: "world", - } - - pv := SettingValueToProto(sv) - - require.NotNil(t, pv) - assert.Equal(t, "world", pv.GetStringValue()) -} - -func TestSettingValueToProto_BoolValue(t *testing.T) { - sv := &v1.SettingValue{ - Type: v1.SettingValueBool, - BoolVal: true, - } - - pv := SettingValueToProto(sv) - - require.NotNil(t, pv) - assert.True(t, pv.GetBoolValue()) -} - -func TestSettingValueToProto_IntValue(t *testing.T) { - sv := &v1.SettingValue{ - Type: v1.SettingValueInt, - IntVal: 99, - } - - pv := SettingValueToProto(sv) - - require.NotNil(t, pv) - assert.Equal(t, int64(99), pv.GetIntValue()) -} - -func TestSettingValueToProto_BytesValue(t *testing.T) { - data := []byte{0xCA, 0xFE} - sv := &v1.SettingValue{ - Type: v1.SettingValueBytes, - BytesVal: data, - } - - pv := SettingValueToProto(sv) - - require.NotNil(t, pv) - assert.Equal(t, data, pv.GetBytesValue()) - - data[0] = 0xFF - assert.Equal(t, byte(0xCA), pv.GetBytesValue()[0], "deep copy must isolate proto from SDK") -} - -func TestSettingValueToProto_Nil(t *testing.T) { - pv := SettingValueToProto(nil) - assert.Nil(t, pv) -} - -// --- SettingScope enum mapping --- - -func TestSettingScopeFromProto(t *testing.T) { - tests := []struct { - proto sbv1.SettingScope - want v1.SettingScope - }{ - {sbv1.SettingScope_SETTING_SCOPE_UNSPECIFIED, v1.SettingScopeUnspecified}, - {sbv1.SettingScope_SETTING_SCOPE_SANDBOX, v1.SettingScopeSandbox}, - {sbv1.SettingScope_SETTING_SCOPE_GLOBAL, v1.SettingScopeGlobal}, - {sbv1.SettingScope(999), v1.SettingScope("")}, - } - for _, tt := range tests { - t.Run(tt.proto.String(), func(t *testing.T) { - assert.Equal(t, tt.want, SettingScopeFromProto(tt.proto)) - }) - } -} - -func TestSettingScopeToProto(t *testing.T) { - tests := []struct { - sdk v1.SettingScope - want sbv1.SettingScope - }{ - {v1.SettingScopeUnspecified, sbv1.SettingScope_SETTING_SCOPE_UNSPECIFIED}, - {v1.SettingScopeSandbox, sbv1.SettingScope_SETTING_SCOPE_SANDBOX}, - {v1.SettingScopeGlobal, sbv1.SettingScope_SETTING_SCOPE_GLOBAL}, - {v1.SettingScope("unknown"), sbv1.SettingScope_SETTING_SCOPE_UNSPECIFIED}, - } - for _, tt := range tests { - t.Run(string(tt.sdk), func(t *testing.T) { - assert.Equal(t, tt.want, SettingScopeToProto(tt.sdk)) - }) - } -} - -// --- PolicySource enum mapping --- - -func TestPolicySourceFromProto(t *testing.T) { - tests := []struct { - proto sbv1.PolicySource - want v1.PolicySource - }{ - {sbv1.PolicySource_POLICY_SOURCE_UNSPECIFIED, v1.PolicySourceUnspecified}, - {sbv1.PolicySource_POLICY_SOURCE_SANDBOX, v1.PolicySourceSandbox}, - {sbv1.PolicySource_POLICY_SOURCE_GLOBAL, v1.PolicySourceGlobal}, - {sbv1.PolicySource(999), v1.PolicySource("")}, - } - for _, tt := range tests { - t.Run(tt.proto.String(), func(t *testing.T) { - assert.Equal(t, tt.want, PolicySourceFromProto(tt.proto)) - }) - } -} - -// --- EffectiveSetting --- - -func TestEffectiveSettingFromProto(t *testing.T) { - pv := &sbv1.EffectiveSetting{ - Value: &sbv1.SettingValue{ - Value: &sbv1.SettingValue_StringValue{StringValue: "val"}, - }, - Scope: sbv1.SettingScope_SETTING_SCOPE_SANDBOX, - } - - es := EffectiveSettingFromProto(pv) - - require.NotNil(t, es) - assert.Equal(t, v1.SettingValueString, es.Value.Type) - assert.Equal(t, "val", es.Value.StringVal) - assert.Equal(t, v1.SettingScopeSandbox, es.Scope) -} - -func TestEffectiveSettingFromProto_NilValue(t *testing.T) { - pv := &sbv1.EffectiveSetting{ - Scope: sbv1.SettingScope_SETTING_SCOPE_GLOBAL, - } - - es := EffectiveSettingFromProto(pv) - - require.NotNil(t, es) - assert.Equal(t, v1.SettingValueType(""), es.Value.Type) - assert.Equal(t, v1.SettingScopeGlobal, es.Scope) -} - -func TestEffectiveSettingFromProto_Nil(t *testing.T) { - es := EffectiveSettingFromProto(nil) - assert.Nil(t, es) -} - -// --- SandboxConfig (GetSandboxConfigResponse → SandboxConfig) --- - -func TestSandboxConfigFromProto(t *testing.T) { - resp := &sbv1.GetSandboxConfigResponse{ - Policy: &sbv1.SandboxPolicy{ - Version: 7, - Filesystem: &sbv1.FilesystemPolicy{ - ReadOnly: []string{"/etc"}, - }, - }, - Version: 3, - PolicyHash: "sha256:abc", - Settings: map[string]*sbv1.EffectiveSetting{ - "timeout": { - Value: &sbv1.SettingValue{ - Value: &sbv1.SettingValue_IntValue{IntValue: 30}, - }, - Scope: sbv1.SettingScope_SETTING_SCOPE_SANDBOX, - }, - "debug": { - Value: &sbv1.SettingValue{ - Value: &sbv1.SettingValue_BoolValue{BoolValue: true}, - }, - Scope: sbv1.SettingScope_SETTING_SCOPE_GLOBAL, - }, - }, - ConfigRevision: 100, - PolicySource: sbv1.PolicySource_POLICY_SOURCE_SANDBOX, - GlobalPolicyVersion: 5, - ProviderEnvRevision: 200, - PolicyValidationFailureMode: "fail_closed", - } - - sc := SandboxConfigFromProto(resp) - - require.NotNil(t, sc) - require.NotNil(t, sc.Policy, "typed SandboxPolicy must be populated") - assert.Equal(t, uint32(7), sc.Policy.Version) - require.NotNil(t, sc.Policy.Filesystem) - assert.Equal(t, []string{"/etc"}, sc.Policy.Filesystem.ReadOnly) - assert.Equal(t, uint32(3), sc.PolicyVersion) - assert.Equal(t, "sha256:abc", sc.PolicyHash) - assert.Equal(t, uint64(100), sc.ConfigRevision) - assert.Equal(t, v1.PolicySourceSandbox, sc.PolicySource) - assert.Equal(t, uint32(5), sc.GlobalPolicyVersion) - assert.Equal(t, uint64(200), sc.ProviderEnvRevision) - assert.Equal(t, "fail_closed", sc.PolicyValidationFailureMode) - - require.Len(t, sc.Settings, 2) - - timeout := sc.Settings["timeout"] - assert.Equal(t, v1.SettingValueInt, timeout.Value.Type) - assert.Equal(t, int64(30), timeout.Value.IntVal) - assert.Equal(t, v1.SettingScopeSandbox, timeout.Scope) - - debug := sc.Settings["debug"] - assert.Equal(t, v1.SettingValueBool, debug.Value.Type) - assert.True(t, debug.Value.BoolVal) - assert.Equal(t, v1.SettingScopeGlobal, debug.Scope) -} - -func TestSandboxConfigFromProto_NilPolicy(t *testing.T) { - resp := &sbv1.GetSandboxConfigResponse{ - Version: 1, - PolicyHash: "sha256:empty", - } - - sc := SandboxConfigFromProto(resp) - - require.NotNil(t, sc) - assert.Nil(t, sc.Policy) - assert.Equal(t, uint32(1), sc.PolicyVersion) - assert.Empty(t, sc.Settings) -} - -func TestSandboxConfigFromProto_Nil(t *testing.T) { - sc := SandboxConfigFromProto(nil) - assert.Nil(t, sc) -} - -func TestSandboxConfigFromProto_SettingsDeepCopy(t *testing.T) { - resp := &sbv1.GetSandboxConfigResponse{ - Settings: map[string]*sbv1.EffectiveSetting{ - "key1": { - Value: &sbv1.SettingValue{ - Value: &sbv1.SettingValue_StringValue{StringValue: "original"}, - }, - Scope: sbv1.SettingScope_SETTING_SCOPE_SANDBOX, - }, - }, - } - - sc := SandboxConfigFromProto(resp) - - require.NotNil(t, sc) - // Mutate the proto map — SDK map must not be affected. - resp.Settings["key1"].Value.Value = &sbv1.SettingValue_StringValue{StringValue: "mutated"} - assert.Equal(t, "original", sc.Settings["key1"].Value.StringVal, - "deep copy must isolate SDK settings from proto") -} - -// --- GatewayConfig (GetGatewayConfigResponse → GatewayConfig) --- - -func TestGatewayConfigFromProto(t *testing.T) { - resp := &sbv1.GetGatewayConfigResponse{ - Settings: map[string]*sbv1.SettingValue{ - "region": { - Value: &sbv1.SettingValue_StringValue{StringValue: "us-west-2"}, - }, - "max_sandboxes": { - Value: &sbv1.SettingValue_IntValue{IntValue: 100}, - }, - }, - SettingsRevision: 42, - } - - gc := GatewayConfigFromProto(resp) - - require.NotNil(t, gc) - assert.Equal(t, uint64(42), gc.SettingsRevision) - require.Len(t, gc.Settings, 2) - - region := gc.Settings["region"] - assert.Equal(t, v1.SettingValueString, region.Type) - assert.Equal(t, "us-west-2", region.StringVal) - - maxSb := gc.Settings["max_sandboxes"] - assert.Equal(t, v1.SettingValueInt, maxSb.Type) - assert.Equal(t, int64(100), maxSb.IntVal) -} - -func TestGatewayConfigFromProto_EmptySettings(t *testing.T) { - resp := &sbv1.GetGatewayConfigResponse{ - SettingsRevision: 1, - } - - gc := GatewayConfigFromProto(resp) - - require.NotNil(t, gc) - assert.Equal(t, uint64(1), gc.SettingsRevision) - assert.Empty(t, gc.Settings) -} - -func TestGatewayConfigFromProto_Nil(t *testing.T) { - gc := GatewayConfigFromProto(nil) - assert.Nil(t, gc) -} - -func TestGatewayConfigFromProto_SettingsDeepCopy(t *testing.T) { - resp := &sbv1.GetGatewayConfigResponse{ - Settings: map[string]*sbv1.SettingValue{ - "key": { - Value: &sbv1.SettingValue_StringValue{StringValue: "original"}, - }, - }, - } - - gc := GatewayConfigFromProto(resp) - - require.NotNil(t, gc) - // Mutate the proto map — SDK map must not be affected. - resp.Settings["key"].Value = &sbv1.SettingValue_StringValue{StringValue: "mutated"} - assert.Equal(t, "original", gc.Settings["key"].StringVal, - "deep copy must isolate SDK settings from proto") -} - -// --- ConfigUpdate (ConfigUpdate → UpdateConfigRequest) --- - -func TestConfigUpdateToProto(t *testing.T) { - cu := &v1.ConfigUpdate{ - Name: "my-sandbox", - SettingKey: "timeout", - SettingValue: &v1.SettingValue{ - Type: v1.SettingValueInt, - IntVal: 60, - }, - DeleteSetting: false, - Global: false, - ExpectedResourceVersion: 7, - Annotations: map[string]string{"source": "cli", "user": "admin"}, - } - - req, err := ConfigUpdateToProto(cu) - require.NoError(t, err) - - require.NotNil(t, req) - assert.Equal(t, "my-sandbox", req.GetSandbox()) - assert.Equal(t, "timeout", req.SettingKey) - require.NotNil(t, req.SettingValue) - assert.Equal(t, int64(60), req.SettingValue.GetIntValue()) - assert.False(t, req.DeleteSetting) - assert.False(t, req.Global) - assert.Equal(t, uint64(7), req.ExpectedResourceVersion) - assert.Nil(t, req.Policy) - assert.Empty(t, req.MergeOperations) - assert.Equal(t, map[string]string{"source": "cli", "user": "admin"}, req.Annotations) - - cu.Annotations["source"] = "MUTATED" - assert.Equal(t, "cli", req.Annotations["source"], "annotations must be deep copied") -} - -func TestConfigUpdateToProto_WithPolicy(t *testing.T) { - cu := &v1.ConfigUpdate{ - Name: "sb-policy", - Policy: &v1.SandboxPolicy{ - Version: 3, - Filesystem: &v1.FilesystemPolicy{ - ReadOnly: []string{"/etc"}, - }, - }, - } - - req, err := ConfigUpdateToProto(cu) - require.NoError(t, err) - - require.NotNil(t, req) - require.NotNil(t, req.Policy, "typed SandboxPolicy must be converted to proto") - assert.Equal(t, uint32(3), req.Policy.GetVersion()) - require.NotNil(t, req.Policy.GetFilesystem()) - assert.Equal(t, []string{"/etc"}, req.Policy.GetFilesystem().GetReadOnly()) -} - -func TestConfigUpdateToProto_WithDeleteSetting(t *testing.T) { - cu := &v1.ConfigUpdate{ - Name: "sb-del", - SettingKey: "obsolete-key", - DeleteSetting: true, - } - - req, err := ConfigUpdateToProto(cu) - require.NoError(t, err) - - require.NotNil(t, req) - assert.Equal(t, "obsolete-key", req.SettingKey) - assert.True(t, req.DeleteSetting) -} - -func TestConfigUpdateToProto_GlobalScope(t *testing.T) { - cu := &v1.ConfigUpdate{ - SettingKey: "global-setting", - SettingValue: &v1.SettingValue{ - Type: v1.SettingValueString, - StringVal: "global-val", - }, - Global: true, - } - - req, err := ConfigUpdateToProto(cu) - require.NoError(t, err) - - require.NotNil(t, req) - assert.True(t, req.Global) - assert.Empty(t, req.GetSandbox()) -} - -func TestConfigUpdateToProto_NilSettingValue(t *testing.T) { - cu := &v1.ConfigUpdate{ - Name: "sb-nil", - SettingKey: "key", - } - - req, err := ConfigUpdateToProto(cu) - require.NoError(t, err) - - require.NotNil(t, req) - assert.Nil(t, req.SettingValue) -} - -func TestConfigUpdateToProto_Nil(t *testing.T) { - req, err := ConfigUpdateToProto(nil) - require.NoError(t, err) - assert.Nil(t, req) -} - -func TestConfigUpdateToProto_NilPolicy(t *testing.T) { - cu := &v1.ConfigUpdate{ - Name: "sb-nil-policy", - } - - req, err := ConfigUpdateToProto(cu) - require.NoError(t, err) - - require.NotNil(t, req) - assert.Nil(t, req.Policy, "nil SDK policy must produce nil proto policy") -} - -// --- ConfigUpdateResult (UpdateConfigResponse → ConfigUpdateResult) --- - -func TestConfigUpdateResultFromProto(t *testing.T) { - resp := &pb.UpdateConfigResponse{ - Version: 10, - PolicyHash: "sha256:updated", - SettingsRevision: 55, - Deleted: true, - Annotations: map[string]string{"sandbox_id": "sb-123"}, - } - - result := ConfigUpdateResultFromProto(resp) - - require.NotNil(t, result) - assert.Equal(t, uint32(10), result.Version) - assert.Equal(t, "sha256:updated", result.PolicyHash) - assert.Equal(t, uint64(55), result.SettingsRevision) - assert.True(t, result.Deleted) - assert.Equal(t, map[string]string{"sandbox_id": "sb-123"}, result.Annotations) - - resp.Annotations["sandbox_id"] = "MUTATED" - assert.Equal(t, "sb-123", result.Annotations["sandbox_id"], "annotations must be deep copied") -} - -func TestConfigUpdateResultFromProto_DefaultValues(t *testing.T) { - resp := &pb.UpdateConfigResponse{} - - result := ConfigUpdateResultFromProto(resp) - - require.NotNil(t, result) - assert.Zero(t, result.Version) - assert.Empty(t, result.PolicyHash) - assert.Zero(t, result.SettingsRevision) - assert.False(t, result.Deleted) -} - -func TestConfigUpdateResultFromProto_Nil(t *testing.T) { - result := ConfigUpdateResultFromProto(nil) - assert.Nil(t, result) -} - -// --- PolicyMergeOperationToProto --- - -func TestPolicyMergeOperationToProto_Nil(t *testing.T) { - pmo, err := PolicyMergeOperationToProto(nil) - assert.NoError(t, err) - assert.Nil(t, pmo) -} - -func TestPolicyMergeOperationToProto_Empty(t *testing.T) { - op := &v1.PolicyMergeOperation{} - _, err := PolicyMergeOperationToProto(op) - - require.Error(t, err) - assert.Contains(t, err.Error(), "got 0") -} - -func TestPolicyMergeOperationToProto_MultipleSet(t *testing.T) { - op := &v1.PolicyMergeOperation{ - AddRule: &v1.AddNetworkRule{RuleName: "r1"}, - RemoveRule: &v1.RemoveNetworkRule{RuleName: "r2"}, - } - _, err := PolicyMergeOperationToProto(op) - - require.Error(t, err) - assert.Contains(t, err.Error(), "got 2") -} - -func TestPolicyMergeOperationToProto_AddRule(t *testing.T) { - op := &v1.PolicyMergeOperation{ - AddRule: &v1.AddNetworkRule{ - RuleName: "allow-api", Rule: v1.NetworkPolicyRule{ - Name: "allow-api", - Endpoints: []v1.PolicyNetworkEndpoint{ - {Host: "api.example.com", Port: 443, Protocol: "tcp"}, - }, - Binaries: []v1.PolicyNetworkBinary{ - {Path: "/usr/bin/curl"}, - }, - }, - }, - } - - pmo, err := PolicyMergeOperationToProto(op) - require.NoError(t, err) - - require.NotNil(t, pmo) - ar := pmo.GetAddRule() - require.NotNil(t, ar, "expected AddRule variant") - assert.Equal(t, "allow-api", ar.GetRuleName()) - require.NotNil(t, ar.GetRule()) - assert.Equal(t, "allow-api", ar.GetRule().GetName()) - require.Len(t, ar.GetRule().GetEndpoints(), 1) - assert.Equal(t, "api.example.com", ar.GetRule().GetEndpoints()[0].GetHost()) - assert.Equal(t, uint32(443), ar.GetRule().GetEndpoints()[0].GetPort()) - require.Len(t, ar.GetRule().GetBinaries(), 1) - assert.Equal(t, "/usr/bin/curl", ar.GetRule().GetBinaries()[0].GetPath()) -} - -func TestPolicyMergeOperationToProto_RemoveEndpoint(t *testing.T) { - op := &v1.PolicyMergeOperation{ - RemoveEndpoint: &v1.RemoveNetworkEndpoint{ - RuleName: "allow-api", Host: "old.example.com", - Port: 8080, - }, - } - - pmo, err := PolicyMergeOperationToProto(op) - require.NoError(t, err) - - require.NotNil(t, pmo) - re := pmo.GetRemoveEndpoint() - require.NotNil(t, re, "expected RemoveEndpoint variant") - assert.Equal(t, "allow-api", re.GetRuleName()) - assert.Equal(t, "old.example.com", re.GetHost()) - assert.Equal(t, uint32(8080), re.GetPort()) -} - -func TestPolicyMergeOperationToProto_RemoveRule(t *testing.T) { - op := &v1.PolicyMergeOperation{ - RemoveRule: &v1.RemoveNetworkRule{ - RuleName: "obsolete-rule"}, - } - - pmo, err := PolicyMergeOperationToProto(op) - require.NoError(t, err) - - require.NotNil(t, pmo) - rr := pmo.GetRemoveRule() - require.NotNil(t, rr, "expected RemoveRule variant") - assert.Equal(t, "obsolete-rule", rr.GetRuleName()) -} - -func TestPolicyMergeOperationToProto_AddDenyRules(t *testing.T) { - path := "/api" - op := &v1.PolicyMergeOperation{ - AddDenyRules: &v1.AddDenyRules{ - Target: &v1.L7RuleTarget{ - RuleName: "blocked-api", Host: "blocked.example.com", - Ports: []uint32{443, 8443}, - Path: &path, - Binaries: []v1.PolicyNetworkBinary{{Path: "/usr/bin/curl"}, {Path: "/usr/bin/wget"}}, - }, - DenyRules: []v1.L7DenyRule{ - { - Method: "POST", - Path: "/admin", - }, - { - Method: "GET", - OperationType: "query", - OperationName: "InternalData", - }, - }, - }, - } - - pmo, err := PolicyMergeOperationToProto(op) - require.NoError(t, err) - - require.NotNil(t, pmo) - adr := pmo.GetAddDenyRules() - require.NotNil(t, adr, "expected AddDenyRules variant") - require.NotNil(t, adr.GetTarget()) - assert.Equal(t, "blocked-api", adr.GetTarget().GetRuleName()) - assert.Equal(t, "blocked.example.com", adr.GetTarget().GetHost()) - assert.Equal(t, []uint32{443, 8443}, adr.GetTarget().GetPorts()) - require.NotNil(t, adr.GetTarget().Path) - assert.Equal(t, "/api", adr.GetTarget().GetPath()) - require.Len(t, adr.GetTarget().GetBinaries(), 2) - assert.Equal(t, "/usr/bin/curl", adr.GetTarget().GetBinaries()[0].GetPath()) - assert.Equal(t, "/usr/bin/wget", adr.GetTarget().GetBinaries()[1].GetPath()) - assert.False(t, adr.GetTarget().GetAnyBinary()) - require.Len(t, adr.GetDenyRules(), 2) - assert.Equal(t, "POST", adr.GetDenyRules()[0].GetMethod()) - assert.Equal(t, "/admin", adr.GetDenyRules()[0].GetPath()) - assert.Equal(t, "InternalData", adr.GetDenyRules()[1].GetOperationName()) -} - -func TestPolicyMergeOperationToProto_AddAllowRules(t *testing.T) { - op := &v1.PolicyMergeOperation{ - AddAllowRules: &v1.AddAllowRules{ - Target: &v1.L7RuleTarget{ - RuleName: "public-api", Host: "api.example.com", - Ports: []uint32{443}, - AnyBinary: true, - }, - Rules: []v1.L7Rule{ - { - Allow: &v1.L7Allow{ - Method: "GET", - Path: "/health", - }, - }, - }, - }, - } - - pmo, err := PolicyMergeOperationToProto(op) - require.NoError(t, err) - - require.NotNil(t, pmo) - aar := pmo.GetAddAllowRules() - require.NotNil(t, aar, "expected AddAllowRules variant") - require.NotNil(t, aar.GetTarget()) - assert.Equal(t, "public-api", aar.GetTarget().GetRuleName()) - assert.Equal(t, "api.example.com", aar.GetTarget().GetHost()) - assert.Equal(t, []uint32{443}, aar.GetTarget().GetPorts()) - assert.Nil(t, aar.GetTarget().Path) - assert.Empty(t, aar.GetTarget().GetBinaries()) - assert.True(t, aar.GetTarget().GetAnyBinary()) - require.Len(t, aar.GetRules(), 1) - require.NotNil(t, aar.GetRules()[0].GetAllow()) - assert.Equal(t, "GET", aar.GetRules()[0].GetAllow().GetMethod()) - assert.Equal(t, "/health", aar.GetRules()[0].GetAllow().GetPath()) -} - -func TestPolicyMergeOperationToProto_L7TargetDeepCopy(t *testing.T) { - for _, kind := range []string{"allow", "deny"} { - t.Run(kind, func(t *testing.T) { - path := "" - target := &v1.L7RuleTarget{ - RuleName: "api", Host: "api.example.com", - Ports: []uint32{443, 8443}, - Path: &path, - Binaries: []v1.PolicyNetworkBinary{{Path: "/usr/bin/curl"}}, - } - op := &v1.PolicyMergeOperation{} - if kind == "allow" { - op.AddAllowRules = &v1.AddAllowRules{Target: target} - } else { - op.AddDenyRules = &v1.AddDenyRules{Target: target} - } - converted, err := PolicyMergeOperationToProto(op) - require.NoError(t, err) - wireTarget := converted.GetAddAllowRules().GetTarget() - if kind == "deny" { - wireTarget = converted.GetAddDenyRules().GetTarget() - } - require.NotNil(t, wireTarget) - require.NotNil(t, wireTarget.Path) - assert.Empty(t, *wireTarget.Path, "explicit empty path must retain presence") - - // Editing the caller's declaration must not change the queued request. - target.Ports[0] = 80 - target.Binaries[0].Path = "/usr/bin/python" - path = "/changed" - assert.Equal(t, []uint32{443, 8443}, wireTarget.GetPorts()) - assert.Equal(t, "/usr/bin/curl", wireTarget.GetBinaries()[0].GetPath()) - assert.Empty(t, *wireTarget.Path) - - // Editing the wire request must not mutate the caller's objects either. - wireTarget.Ports[1] = 8080 - wireTarget.Binaries[0].Path = "/usr/bin/node" - *wireTarget.Path = "/wire" - assert.Equal(t, []uint32{80, 8443}, target.Ports) - assert.Equal(t, "/usr/bin/python", target.Binaries[0].Path) - assert.Equal(t, "/changed", path) - }) - } -} - -func TestPolicyMergeOperationToProto_L7TargetDoesNotInferScope(t *testing.T) { - for _, kind := range []string{"allow", "deny"} { - for _, tc := range []struct { - name string - target *v1.L7RuleTarget - }{ - {name: "missing"}, - {name: "omitted_scope", target: &v1.L7RuleTarget{RuleName: "api", Host: "api.example.com"}}, - } { - t.Run(kind+"/"+tc.name, func(t *testing.T) { - op := &v1.PolicyMergeOperation{} - if kind == "allow" { - op.AddAllowRules = &v1.AddAllowRules{Target: tc.target} - } else { - op.AddDenyRules = &v1.AddDenyRules{Target: tc.target} - } - converted, err := PolicyMergeOperationToProto(op) - require.NoError(t, err) - target := converted.GetAddAllowRules().GetTarget() - if kind == "deny" { - target = converted.GetAddDenyRules().GetTarget() - } - if tc.target == nil { - assert.Nil(t, target) - return - } - require.NotNil(t, target) - assert.False(t, target.GetAnyBinary()) - assert.Nil(t, target.GetBinaries()) - assert.Nil(t, target.GetPorts()) - assert.Nil(t, target.Path) - }) - } - } -} - -func TestPolicyMergeOperationToProto_RemoveBinary(t *testing.T) { - op := &v1.PolicyMergeOperation{ - RemoveBinary: &v1.RemoveNetworkBinary{ - RuleName: "allow-api", BinaryPath: "/usr/bin/wget", - }, - } - - pmo, err := PolicyMergeOperationToProto(op) - require.NoError(t, err) - - require.NotNil(t, pmo) - rb := pmo.GetRemoveBinary() - require.NotNil(t, rb, "expected RemoveBinary variant") - assert.Equal(t, "allow-api", rb.GetRuleName()) - assert.Equal(t, "/usr/bin/wget", rb.GetBinaryPath()) -} - -// --- ConfigUpdateToProto with MergeOperations --- - -func TestConfigUpdateToProto_WithMergeOperations(t *testing.T) { - cu := &v1.ConfigUpdate{ - Name: "my-sandbox", - MergeOperations: []v1.PolicyMergeOperation{ - { - RemoveRule: &v1.RemoveNetworkRule{RuleName: "old-rule"}, - }, - { - AddRule: &v1.AddNetworkRule{ - RuleName: "new-rule", Rule: v1.NetworkPolicyRule{ - Name: "new-rule", - Endpoints: []v1.PolicyNetworkEndpoint{ - {Host: "svc.local", Port: 8080}, - }, - }, - }, - }, - { - RemoveBinary: &v1.RemoveNetworkBinary{ - RuleName: "new-rule", BinaryPath: "/tmp/bad", - }, - }, - }, - } - - req, err := ConfigUpdateToProto(cu) - require.NoError(t, err) - - require.NotNil(t, req) - assert.Equal(t, "my-sandbox", req.GetSandbox()) - require.Len(t, req.GetMergeOperations(), 3) - - // First: RemoveRule - rr := req.GetMergeOperations()[0].GetRemoveRule() - require.NotNil(t, rr) - assert.Equal(t, "old-rule", rr.GetRuleName()) - - // Second: AddRule - ar := req.GetMergeOperations()[1].GetAddRule() - require.NotNil(t, ar) - assert.Equal(t, "new-rule", ar.GetRuleName()) - require.NotNil(t, ar.GetRule()) - require.Len(t, ar.GetRule().GetEndpoints(), 1) - assert.Equal(t, "svc.local", ar.GetRule().GetEndpoints()[0].GetHost()) - - // Third: RemoveBinary - rb := req.GetMergeOperations()[2].GetRemoveBinary() - require.NotNil(t, rb) - assert.Equal(t, "/tmp/bad", rb.GetBinaryPath()) -} - -func TestConfigUpdateToProto_EmptyMergeOperations(t *testing.T) { - cu := &v1.ConfigUpdate{ - Name: "sb", - MergeOperations: []v1.PolicyMergeOperation{}, - } - - req, err := ConfigUpdateToProto(cu) - require.NoError(t, err) - - require.NotNil(t, req) - assert.Empty(t, req.GetMergeOperations()) -} - -// --- CopyByteSlice helper --- - -func TestCopyByteSlice(t *testing.T) { - original := []byte{0x01, 0x02, 0x03} - copied := CopyByteSlice(original) - - assert.Equal(t, original, copied) - - // Mutate original — copy must not be affected. - original[0] = 0xFF - assert.Equal(t, byte(0x01), copied[0], "copy must be independent of original") -} - -func TestCopyByteSlice_Nil(t *testing.T) { - copied := CopyByteSlice(nil) - assert.Nil(t, copied) -} - -func TestCopyByteSlice_Empty(t *testing.T) { - original := []byte{} - copied := CopyByteSlice(original) - assert.NotNil(t, copied) - assert.Empty(t, copied) -} diff --git a/sdk/go/openshell/v1/internal/converter/ssh.go b/sdk/go/openshell/v1/internal/converter/ssh.go deleted file mode 100644 index e437d8808d..0000000000 --- a/sdk/go/openshell/v1/internal/converter/ssh.go +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" -) - -// SSHSessionFromProto converts a CreateSshSessionResponse to an SSHSession. -func SSHSessionFromProto(resp *pb.CreateSshSessionResponse) *v1.SSHSession { - if resp == nil { - return nil - } - return &v1.SSHSession{ - SandboxID: resp.GetSandboxId(), - Token: resp.GetToken(), - GatewayHost: resp.GetGatewayHost(), - GatewayPort: resp.GetGatewayPort(), - GatewayScheme: resp.GetGatewayScheme(), - HostKeyFingerprint: resp.GetHostKeyFingerprint(), - ExpiresAtMs: MillisFromProto(resp.GetExpirationTime()), - } -} - -// SSHSessionToProto converts an SSHSession to a CreateSshSessionResponse. -// This is primarily used for round-trip testing and fake implementations. -func SSHSessionToProto(session *v1.SSHSession) *pb.CreateSshSessionResponse { - if session == nil { - return nil - } - return &pb.CreateSshSessionResponse{ - SandboxId: session.SandboxID, - Token: session.Token, - GatewayHost: session.GatewayHost, - GatewayPort: session.GatewayPort, - GatewayScheme: session.GatewayScheme, - HostKeyFingerprint: session.HostKeyFingerprint, - ExpirationTime: TimestampFromMillis(session.ExpiresAtMs), - } -} diff --git a/sdk/go/openshell/v1/internal/converter/ssh_test.go b/sdk/go/openshell/v1/internal/converter/ssh_test.go deleted file mode 100644 index dbff7f194e..0000000000 --- a/sdk/go/openshell/v1/internal/converter/ssh_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestSSHSessionFromProto(t *testing.T) { - resp := &pb.CreateSshSessionResponse{ - SandboxId: "sb-123", - Token: "tok-secret", - GatewayHost: "gw.example.com", - GatewayPort: 2222, - GatewayScheme: "https", - HostKeyFingerprint: "SHA256:abc123", - ExpirationTime: TimestampFromMillis(1700000000000), - } - - session := SSHSessionFromProto(resp) - - require.NotNil(t, session) - assert.Equal(t, "sb-123", session.SandboxID) - assert.Equal(t, "tok-secret", session.Token) - assert.Equal(t, "gw.example.com", session.GatewayHost) - assert.Equal(t, uint32(2222), session.GatewayPort) - assert.Equal(t, "https", session.GatewayScheme) - assert.Equal(t, "SHA256:abc123", session.HostKeyFingerprint) - assert.Equal(t, int64(1700000000000), session.ExpiresAtMs) -} - -func TestSSHSessionFromProto_MinimalFields(t *testing.T) { - resp := &pb.CreateSshSessionResponse{ - SandboxId: "sb-min", - Token: "tok-min", - GatewayHost: "localhost", - GatewayPort: 22, - } - - session := SSHSessionFromProto(resp) - - require.NotNil(t, session) - assert.Equal(t, "sb-min", session.SandboxID) - assert.Equal(t, "tok-min", session.Token) - assert.Equal(t, "localhost", session.GatewayHost) - assert.Equal(t, uint32(22), session.GatewayPort) - assert.Empty(t, session.GatewayScheme) - assert.Empty(t, session.HostKeyFingerprint) - assert.Zero(t, session.ExpiresAtMs) -} - -func TestSSHSessionFromProto_Nil(t *testing.T) { - session := SSHSessionFromProto(nil) - assert.Nil(t, session) -} - -func TestSSHSessionToProto(t *testing.T) { - session := &v1.SSHSession{ - SandboxID: "sb-123", - Token: "tok-secret", - GatewayHost: "gw.example.com", - GatewayPort: 2222, - GatewayScheme: "https", - HostKeyFingerprint: "SHA256:abc123", - ExpiresAtMs: 1700000000000, - } - - resp := SSHSessionToProto(session) - - require.NotNil(t, resp) - assert.Equal(t, "sb-123", resp.SandboxId) - assert.Equal(t, "tok-secret", resp.Token) - assert.Equal(t, "gw.example.com", resp.GatewayHost) - assert.Equal(t, uint32(2222), resp.GatewayPort) - assert.Equal(t, "https", resp.GatewayScheme) - assert.Equal(t, "SHA256:abc123", resp.HostKeyFingerprint) - assert.Equal(t, int64(1700000000000), MillisFromProto(resp.ExpirationTime)) -} - -func TestSSHSessionToProto_Nil(t *testing.T) { - resp := SSHSessionToProto(nil) - assert.Nil(t, resp) -} - -func TestSSHSessionRoundTrip(t *testing.T) { - original := &v1.SSHSession{ - SandboxID: "sb-rt", - Token: "tok-rt", - GatewayHost: "rt.example.com", - GatewayPort: 443, - GatewayScheme: "https", - HostKeyFingerprint: "SHA256:roundtrip", - ExpiresAtMs: 1800000000000, - } - - proto := SSHSessionToProto(original) - back := SSHSessionFromProto(proto) - - require.NotNil(t, back) - assert.Equal(t, original.SandboxID, back.SandboxID) - assert.Equal(t, original.Token, back.Token) - assert.Equal(t, original.GatewayHost, back.GatewayHost) - assert.Equal(t, original.GatewayPort, back.GatewayPort) - assert.Equal(t, original.GatewayScheme, back.GatewayScheme) - assert.Equal(t, original.HostKeyFingerprint, back.HostKeyFingerprint) - assert.Equal(t, original.ExpiresAtMs, back.ExpiresAtMs) -} diff --git a/sdk/go/openshell/v1/internal/converter/time.go b/sdk/go/openshell/v1/internal/converter/time.go deleted file mode 100644 index 1a304bc11f..0000000000 --- a/sdk/go/openshell/v1/internal/converter/time.go +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "time" - - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/timestamppb" -) - -// TimeFromProto converts a valid protobuf timestamp to UTC time. -func TimeFromProto(value *timestamppb.Timestamp) time.Time { - if value == nil || value.CheckValid() != nil { - return time.Time{} - } - return value.AsTime().UTC() -} - -// TimePtrFromProto converts a valid protobuf timestamp to a UTC time pointer. -func TimePtrFromProto(value *timestamppb.Timestamp) *time.Time { - if value == nil || value.CheckValid() != nil { - return nil - } - converted := value.AsTime().UTC() - return &converted -} - -// TimestampFromTime converts a non-zero time to a protobuf timestamp. -func TimestampFromTime(value time.Time) *timestamppb.Timestamp { - if value.IsZero() { - return nil - } - return timestamppb.New(value) -} - -// TimestampFromTimePtr converts a non-nil time pointer to a protobuf timestamp. -func TimestampFromTimePtr(value *time.Time) *timestamppb.Timestamp { - if value == nil { - return nil - } - return timestamppb.New(*value) -} - -// MillisFromProto converts a valid protobuf timestamp to Unix milliseconds. -func MillisFromProto(value *timestamppb.Timestamp) int64 { - converted := TimeFromProto(value) - if converted.IsZero() { - return 0 - } - return converted.UnixMilli() -} - -// TimestampFromMillis converts non-zero Unix milliseconds to a protobuf timestamp. -func TimestampFromMillis(value int64) *timestamppb.Timestamp { - if value == 0 { - return nil - } - return timestamppb.New(time.UnixMilli(value)) -} - -// TimestampStringFromProto formats a valid protobuf timestamp as RFC 3339. -func TimestampStringFromProto(value *timestamppb.Timestamp) string { - if value == nil || value.CheckValid() != nil { - return "" - } - return value.AsTime().UTC().Format(time.RFC3339Nano) -} - -// DurationSecondsFromProto converts a valid non-negative protobuf duration to seconds. -func DurationSecondsFromProto(value *durationpb.Duration) uint64 { - if value == nil || value.CheckValid() != nil || value.AsDuration() < 0 { - return 0 - } - return uint64(value.AsDuration() / time.Second) -} - -// DurationFromSeconds converts non-zero seconds to a protobuf duration. -func DurationFromSeconds(value uint64) *durationpb.Duration { - if value == 0 || value > uint64((time.Duration(1<<63-1))/time.Second) { - return nil - } - return durationpb.New(time.Duration(value) * time.Second) -} - -// TimeFromMillis converts a millisecond epoch timestamp to time.Time. -// A zero value returns the zero time. -func TimeFromMillis(ms int64) time.Time { - if ms == 0 { - return time.Time{} - } - return time.UnixMilli(ms).UTC() -} - -// MillisFromTime converts a time.Time to a millisecond epoch timestamp. -// A zero time returns 0. -func MillisFromTime(t time.Time) int64 { - if t.IsZero() { - return 0 - } - return t.UnixMilli() -} - -// TimeFromMillisPtr converts a millisecond epoch timestamp to a *time.Time. -// A zero value returns nil (the resource is not being deleted). -func TimeFromMillisPtr(ms int64) *time.Time { - if ms == 0 { - return nil - } - t := time.UnixMilli(ms).UTC() - return &t -} - -// MillisFromTimePtr converts a *time.Time to a millisecond epoch timestamp. -// A nil pointer returns 0. -func MillisFromTimePtr(t *time.Time) int64 { - if t == nil { - return 0 - } - return t.UnixMilli() -} diff --git a/sdk/go/openshell/v1/internal/converter/time_test.go b/sdk/go/openshell/v1/internal/converter/time_test.go deleted file mode 100644 index f0f6bd421b..0000000000 --- a/sdk/go/openshell/v1/internal/converter/time_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "google.golang.org/protobuf/types/known/timestamppb" -) - -func TestTimePtrProtoRoundTripPreservesMinimumTimestamp(t *testing.T) { - minimum := ×tamppb.Timestamp{Seconds: -62_135_596_800} - - converted := TimePtrFromProto(minimum) - assert.NotNil(t, converted) - assert.True(t, converted.IsZero()) - assert.Equal(t, minimum, TimestampFromTimePtr(converted)) -} - -func TestTimestampStringFromProtoPreservesMinimumTimestamp(t *testing.T) { - minimum := ×tamppb.Timestamp{Seconds: -62_135_596_800} - - assert.Equal(t, "0001-01-01T00:00:00Z", TimestampStringFromProto(minimum)) - assert.Empty(t, TimestampStringFromProto(nil)) - assert.Empty(t, TimestampStringFromProto(×tamppb.Timestamp{Nanos: -1})) -} - -func TestTimeFromMillis(t *testing.T) { - ms := int64(1719475200000) // 2024-06-27T12:00:00Z - tm := TimeFromMillis(ms) - assert.Equal(t, 2024, tm.Year()) - assert.Equal(t, time.June, tm.Month()) - assert.Equal(t, 27, tm.Day()) -} - -func TestTimeFromMillis_Zero(t *testing.T) { - tm := TimeFromMillis(0) - assert.True(t, tm.IsZero()) -} - -func TestMillisFromTime(t *testing.T) { - tm := time.Date(2024, time.June, 27, 12, 0, 0, 0, time.UTC) - ms := MillisFromTime(tm) - assert.Equal(t, int64(1719489600000), ms) -} - -func TestMillisFromTime_Zero(t *testing.T) { - ms := MillisFromTime(time.Time{}) - assert.Equal(t, int64(0), ms) -} - -func TestRoundTrip(t *testing.T) { - original := time.Date(2025, time.March, 15, 10, 30, 0, 0, time.UTC) - ms := MillisFromTime(original) - restored := TimeFromMillis(ms) - assert.Equal(t, original.Unix(), restored.Unix()) -} - -func TestTimeFromMillisPtr_NonZero(t *testing.T) { - ms := int64(1719475200000) - tp := TimeFromMillisPtr(ms) - assert.NotNil(t, tp) - assert.Equal(t, 2024, tp.Year()) -} - -func TestTimeFromMillisPtr_Zero(t *testing.T) { - tp := TimeFromMillisPtr(0) - assert.Nil(t, tp) -} - -func TestMillisFromTimePtr_NonNil(t *testing.T) { - tm := time.Date(2024, time.June, 27, 12, 0, 0, 0, time.UTC) - ms := MillisFromTimePtr(&tm) - assert.Equal(t, int64(1719489600000), ms) -} - -func TestMillisFromTimePtr_Nil(t *testing.T) { - ms := MillisFromTimePtr(nil) - assert.Equal(t, int64(0), ms) -} - -func TestPtrRoundTrip(t *testing.T) { - original := time.Date(2025, time.March, 15, 10, 30, 0, 0, time.UTC) - ms := MillisFromTimePtr(&original) - restored := TimeFromMillisPtr(ms) - assert.NotNil(t, restored) - assert.Equal(t, original.Unix(), restored.Unix()) -} diff --git a/sdk/go/openshell/v1/internal/converter/workspace.go b/sdk/go/openshell/v1/internal/converter/workspace.go deleted file mode 100644 index 71e6129345..0000000000 --- a/sdk/go/openshell/v1/internal/converter/workspace.go +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" -) - -// WorkspaceFromProto converts a proto Workspace to an SDK Workspace. -func WorkspaceFromProto(w *dm.Workspace) *types.Workspace { - if w == nil { - return nil - } - - result := &types.Workspace{} - - if m := w.GetMetadata(); m != nil { - result.ID = m.GetId() - result.Name = m.GetName() - result.CreatedAt = TimeFromProto(m.GetCreatedTime()) - result.Labels = CopyStringMap(m.GetLabels()) - result.Annotations = CopyStringMap(m.GetAnnotations()) - result.ResourceVersion = m.GetResourceVersion() - result.Workspace = m.GetWorkspace() - result.DeletionTimestamp = TimePtrFromProto(m.GetDeletionTime()) - } - - if status := w.GetStatus(); status != nil { - result.Phase = WorkspacePhaseFromProto(status.GetPhase()) - } else { - result.Phase = types.WorkspaceUnknown - } - - return result -} - -// WorkspacePhaseFromProto converts a proto WorkspacePhase to an SDK WorkspacePhase. -func WorkspacePhaseFromProto(phase dm.WorkspacePhase) types.WorkspacePhase { - switch phase { - case dm.WorkspacePhase_WORKSPACE_PHASE_ACTIVE: - return types.WorkspaceActive - case dm.WorkspacePhase_WORKSPACE_PHASE_TERMINATING: - return types.WorkspaceTerminating - default: - return types.WorkspaceUnknown - } -} - -// WorkspaceMemberFromProto converts a proto WorkspaceMember to an SDK WorkspaceMember. -func WorkspaceMemberFromProto(m *pb.WorkspaceMember) *types.WorkspaceMember { - if m == nil { - return nil - } - - result := &types.WorkspaceMember{ - PrincipalSubject: m.GetPrincipalSubject(), - Role: WorkspaceRoleFromProto(m.GetRole()), - } - - if meta := m.GetMetadata(); meta != nil { - result.ID = meta.GetId() - result.Name = meta.GetName() - result.CreatedAt = TimeFromProto(meta.GetCreatedTime()) - result.Labels = CopyStringMap(meta.GetLabels()) - result.Annotations = CopyStringMap(meta.GetAnnotations()) - result.ResourceVersion = meta.GetResourceVersion() - } - - return result -} - -// WorkspaceRoleFromProto converts a proto WorkspaceRole to an SDK WorkspaceRole. -func WorkspaceRoleFromProto(role pb.WorkspaceRole) types.WorkspaceRole { - switch role { - case pb.WorkspaceRole_WORKSPACE_ROLE_ADMIN: - return types.WorkspaceRoleAdmin - case pb.WorkspaceRole_WORKSPACE_ROLE_USER: - return types.WorkspaceRoleUser - default: - return types.WorkspaceRoleUnknown - } -} - -// WorkspaceRoleToProto converts an SDK WorkspaceRole to a proto WorkspaceRole. -func WorkspaceRoleToProto(role types.WorkspaceRole) pb.WorkspaceRole { - switch role { - case types.WorkspaceRoleAdmin: - return pb.WorkspaceRole_WORKSPACE_ROLE_ADMIN - case types.WorkspaceRoleUser: - return pb.WorkspaceRole_WORKSPACE_ROLE_USER - default: - return pb.WorkspaceRole_WORKSPACE_ROLE_UNSPECIFIED - } -} diff --git a/sdk/go/openshell/v1/internal/converter/workspace_test.go b/sdk/go/openshell/v1/internal/converter/workspace_test.go deleted file mode 100644 index 9d39f25fed..0000000000 --- a/sdk/go/openshell/v1/internal/converter/workspace_test.go +++ /dev/null @@ -1,209 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package converter - -import ( - "testing" - "time" - - v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestWorkspaceFromProto(t *testing.T) { - proto := &dm.Workspace{ - Metadata: &dm.ObjectMeta{ - Id: "ws-1", - Name: "my-workspace", - CreatedTime: TimestampFromMillis(1700000000000), - Labels: map[string]string{"team": "platform"}, - Annotations: map[string]string{"managed-by": "sdk"}, - ResourceVersion: 3, - Workspace: "", - DeletionTime: TimestampFromMillis(1700000060000), - }, - Status: &dm.WorkspaceStatus{ - Phase: dm.WorkspacePhase_WORKSPACE_PHASE_ACTIVE, - }, - } - - ws := WorkspaceFromProto(proto) - - require.NotNil(t, ws) - assert.Equal(t, "ws-1", ws.ID) - assert.Equal(t, "my-workspace", ws.Name) - assert.Equal(t, time.UnixMilli(1700000000000).UTC(), ws.CreatedAt) - assert.Equal(t, map[string]string{"team": "platform"}, ws.Labels) - assert.Equal(t, map[string]string{"managed-by": "sdk"}, ws.Annotations) - assert.Equal(t, uint64(3), ws.ResourceVersion) - assert.Equal(t, "", ws.Workspace) - require.NotNil(t, ws.DeletionTimestamp) - assert.Equal(t, time.UnixMilli(1700000060000).UTC(), *ws.DeletionTimestamp) - assert.Equal(t, v1.WorkspaceActive, ws.Phase) -} - -func TestWorkspaceFromProto_DeepCopy(t *testing.T) { - labels := map[string]string{"env": "test"} - proto := &dm.Workspace{ - Metadata: &dm.ObjectMeta{ - Name: "ws-copy", - Labels: labels, - }, - Status: &dm.WorkspaceStatus{ - Phase: dm.WorkspacePhase_WORKSPACE_PHASE_ACTIVE, - }, - } - - ws := WorkspaceFromProto(proto) - labels["env"] = "mutated" - - assert.Equal(t, "test", ws.Labels["env"]) -} - -func TestWorkspaceFromProto_NilMetadata(t *testing.T) { - proto := &dm.Workspace{ - Status: &dm.WorkspaceStatus{ - Phase: dm.WorkspacePhase_WORKSPACE_PHASE_TERMINATING, - }, - } - - ws := WorkspaceFromProto(proto) - - require.NotNil(t, ws) - assert.Empty(t, ws.ID) - assert.Equal(t, v1.WorkspaceTerminating, ws.Phase) -} - -func TestWorkspaceFromProto_NilStatus(t *testing.T) { - proto := &dm.Workspace{ - Metadata: &dm.ObjectMeta{Name: "ws-nostatus"}, - } - - ws := WorkspaceFromProto(proto) - - require.NotNil(t, ws) - assert.Equal(t, v1.WorkspaceUnknown, ws.Phase) -} - -func TestWorkspaceFromProto_Nil(t *testing.T) { - ws := WorkspaceFromProto(nil) - assert.Nil(t, ws) -} - -func TestWorkspacePhaseFromProto(t *testing.T) { - tests := []struct { - proto dm.WorkspacePhase - expected v1.WorkspacePhase - }{ - {dm.WorkspacePhase_WORKSPACE_PHASE_ACTIVE, v1.WorkspaceActive}, - {dm.WorkspacePhase_WORKSPACE_PHASE_TERMINATING, v1.WorkspaceTerminating}, - {dm.WorkspacePhase_WORKSPACE_PHASE_UNSPECIFIED, v1.WorkspaceUnknown}, - {dm.WorkspacePhase(99), v1.WorkspaceUnknown}, - } - - for _, tt := range tests { - assert.Equal(t, tt.expected, WorkspacePhaseFromProto(tt.proto)) - } -} - -func TestWorkspaceMemberFromProto(t *testing.T) { - proto := &pb.WorkspaceMember{ - Metadata: &dm.ObjectMeta{ - Id: "mem-1", - Name: "member-auto-name", - CreatedTime: TimestampFromMillis(1700000000000), - Annotations: map[string]string{"source": "cli"}, - ResourceVersion: 2, - }, - PrincipalSubject: "user@example.com", - Role: pb.WorkspaceRole_WORKSPACE_ROLE_ADMIN, - } - - m := WorkspaceMemberFromProto(proto) - - require.NotNil(t, m) - assert.Equal(t, "mem-1", m.ID) - assert.Equal(t, "member-auto-name", m.Name) - assert.Equal(t, time.UnixMilli(1700000000000).UTC(), m.CreatedAt) - assert.Equal(t, map[string]string{"source": "cli"}, m.Annotations) - assert.Equal(t, uint64(2), m.ResourceVersion) - assert.Equal(t, "user@example.com", m.PrincipalSubject) - assert.Equal(t, v1.WorkspaceRoleAdmin, m.Role) -} - -func TestWorkspaceMemberFromProto_DeepCopy(t *testing.T) { - annotations := map[string]string{"key": "original"} - proto := &pb.WorkspaceMember{ - Metadata: &dm.ObjectMeta{ - Annotations: annotations, - }, - PrincipalSubject: "user@test.com", - Role: pb.WorkspaceRole_WORKSPACE_ROLE_USER, - } - - m := WorkspaceMemberFromProto(proto) - annotations["key"] = "mutated" - - assert.Equal(t, "original", m.Annotations["key"]) -} - -func TestWorkspaceMemberFromProto_NilMetadata(t *testing.T) { - proto := &pb.WorkspaceMember{ - PrincipalSubject: "user@test.com", - Role: pb.WorkspaceRole_WORKSPACE_ROLE_USER, - } - - m := WorkspaceMemberFromProto(proto) - - require.NotNil(t, m) - assert.Empty(t, m.ID) - assert.Equal(t, "user@test.com", m.PrincipalSubject) - assert.Equal(t, v1.WorkspaceRoleUser, m.Role) -} - -func TestWorkspaceMemberFromProto_Nil(t *testing.T) { - m := WorkspaceMemberFromProto(nil) - assert.Nil(t, m) -} - -func TestWorkspaceRoleFromProto(t *testing.T) { - tests := []struct { - proto pb.WorkspaceRole - expected v1.WorkspaceRole - }{ - {pb.WorkspaceRole_WORKSPACE_ROLE_ADMIN, v1.WorkspaceRoleAdmin}, - {pb.WorkspaceRole_WORKSPACE_ROLE_USER, v1.WorkspaceRoleUser}, - {pb.WorkspaceRole_WORKSPACE_ROLE_UNSPECIFIED, v1.WorkspaceRoleUnknown}, - {pb.WorkspaceRole(99), v1.WorkspaceRoleUnknown}, - } - - for _, tt := range tests { - assert.Equal(t, tt.expected, WorkspaceRoleFromProto(tt.proto)) - } -} - -func TestWorkspaceRoleToProto(t *testing.T) { - tests := []struct { - sdk v1.WorkspaceRole - expected pb.WorkspaceRole - }{ - {v1.WorkspaceRoleAdmin, pb.WorkspaceRole_WORKSPACE_ROLE_ADMIN}, - {v1.WorkspaceRoleUser, pb.WorkspaceRole_WORKSPACE_ROLE_USER}, - {v1.WorkspaceRole("invalid"), pb.WorkspaceRole_WORKSPACE_ROLE_UNSPECIFIED}, - } - - for _, tt := range tests { - assert.Equal(t, tt.expected, WorkspaceRoleToProto(tt.sdk)) - } -} - -func TestWorkspaceRoleRoundTrip(t *testing.T) { - roles := []v1.WorkspaceRole{v1.WorkspaceRoleAdmin, v1.WorkspaceRoleUser} - for _, role := range roles { - assert.Equal(t, role, WorkspaceRoleFromProto(WorkspaceRoleToProto(role))) - } -} diff --git a/sdk/go/openshell/v1/internal/grpc/conn.go b/sdk/go/openshell/v1/internal/grpc/conn.go deleted file mode 100644 index 43599cf495..0000000000 --- a/sdk/go/openshell/v1/internal/grpc/conn.go +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package grpc provides gRPC connection setup utilities. -package grpc - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "os" - "strings" - - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" -) - -// TLSParams holds TLS settings without importing the v1 package. -type TLSParams struct { - CertFile string - KeyFile string - CAFile string - Insecure bool -} - -// NewConnection creates a gRPC client connection. -// The address may include an http:// or https:// scheme (as written by the -// upstream gateway). The scheme drives transport selection: http:// uses -// plaintext, https:// or no scheme uses TLS. -func NewConnection(address string, tlsCfg *TLSParams, auth credentials.PerRPCCredentials) (*grpc.ClientConn, error) { - usePlaintext := false - if strings.HasPrefix(address, "http://") { - usePlaintext = true - address = strings.TrimPrefix(address, "http://") - } else { - address = strings.TrimPrefix(address, "https://") - } - opts := []grpc.DialOption{} - - if usePlaintext { - if tlsCfg != nil && (tlsCfg.CAFile != "" || tlsCfg.CertFile != "" || tlsCfg.KeyFile != "") { - return nil, fmt.Errorf("grpc connect: TLS parameters (CAFile/CertFile/KeyFile) are ignored with plaintext (http://) address") - } - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) - } else if tlsCfg != nil { - creds, err := buildTLSCredentials(tlsCfg) - if err != nil { - return nil, fmt.Errorf("tls config: %w", err) - } - opts = append(opts, grpc.WithTransportCredentials(creds)) - } else { - opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{MinVersion: tls.VersionTLS12}))) - } - - if auth != nil { - if usePlaintext && auth.RequireTransportSecurity() { - return nil, fmt.Errorf("grpc connect: auth provider requires transport security but address uses plaintext (http://)") - } - opts = append(opts, grpc.WithPerRPCCredentials(auth)) - } - - conn, err := grpc.NewClient(address, opts...) - if err != nil { - return nil, fmt.Errorf("grpc connect: %w", err) - } - return conn, nil -} - -func buildTLSCredentials(cfg *TLSParams) (credentials.TransportCredentials, error) { - tlsConfig := &tls.Config{ - MinVersion: tls.VersionTLS12, - InsecureSkipVerify: cfg.Insecure, //nolint:gosec // user-requested skip for dev gateways - } - - if cfg.CAFile != "" { - caCert, err := os.ReadFile(cfg.CAFile) - if err != nil { - return nil, fmt.Errorf("read CA file: %w", err) - } - pool := x509.NewCertPool() - if !pool.AppendCertsFromPEM(caCert) { - return nil, fmt.Errorf("invalid CA certificate") - } - tlsConfig.RootCAs = pool - } - - if cfg.CertFile != "" && cfg.KeyFile != "" { - cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile) - if err != nil { - return nil, fmt.Errorf("load client cert: %w", err) - } - tlsConfig.Certificates = []tls.Certificate{cert} - } else if cfg.CertFile != "" || cfg.KeyFile != "" { - return nil, fmt.Errorf("both CertFile and KeyFile must be provided for client certificate authentication") - } - - return credentials.NewTLS(tlsConfig), nil -} diff --git a/sdk/go/openshell/v1/internal/grpc/conn_test.go b/sdk/go/openshell/v1/internal/grpc/conn_test.go deleted file mode 100644 index 6f03003fbf..0000000000 --- a/sdk/go/openshell/v1/internal/grpc/conn_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package grpc - -import ( - "context" - "net" - "testing" - - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -func TestNewConnectionHTTPSchemeUsesPlaintext(t *testing.T) { - lis, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - defer func() { _ = lis.Close() }() - - srv := grpc.NewServer() - go func() { _ = srv.Serve(lis) }() - defer srv.Stop() - - conn, err := NewConnection("http://"+lis.Addr().String(), nil, nil) - require.NoError(t, err) - defer func() { _ = conn.Close() }() -} - -func TestNewConnectionHTTPSSchemeUsesTLS(t *testing.T) { - conn, err := NewConnection("https://127.0.0.1:1", nil, nil) - require.NoError(t, err) - defer func() { _ = conn.Close() }() -} - -func TestNewConnectionNoSchemeUsesTLS(t *testing.T) { - conn, err := NewConnection("127.0.0.1:1", nil, nil) - require.NoError(t, err) - defer func() { _ = conn.Close() }() -} - -func TestNewConnectionInsecureTLSConfig(t *testing.T) { - conn, err := NewConnection("127.0.0.1:1", &TLSParams{Insecure: true}, nil) - require.NoError(t, err) - defer func() { _ = conn.Close() }() -} - -func TestNewConnectionHTTPWithSecureAuthRejects(t *testing.T) { - auth := &testTokenAuth{token: "dev-token", requireSecurity: true} - _, err := NewConnection("http://127.0.0.1:1", nil, auth) - require.Error(t, err) -} - -func TestNewConnectionHTTPWithInsecureAuth(t *testing.T) { - lis, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - defer func() { _ = lis.Close() }() - - srv := grpc.NewServer(grpc.Creds(insecure.NewCredentials())) - go func() { _ = srv.Serve(lis) }() - defer srv.Stop() - - auth := &testTokenAuth{token: "dev-token", requireSecurity: false} - conn, err := NewConnection("http://"+lis.Addr().String(), nil, auth) - require.NoError(t, err) - defer func() { _ = conn.Close() }() -} - -func TestNewConnectionHTTPWithTLSParamsRejects(t *testing.T) { - _, err := NewConnection("http://127.0.0.1:1", &TLSParams{CAFile: "/some/ca.pem"}, nil) - require.Error(t, err) - require.Contains(t, err.Error(), "TLS parameters") -} - -func TestNewConnectionHTTPWithEmptyTLSParamsAllowed(t *testing.T) { - lis, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - defer func() { _ = lis.Close() }() - - srv := grpc.NewServer() - go func() { _ = srv.Serve(lis) }() - defer srv.Stop() - - conn, err := NewConnection("http://"+lis.Addr().String(), &TLSParams{}, nil) - require.NoError(t, err) - defer func() { _ = conn.Close() }() -} - -type testTokenAuth struct { - token string - requireSecurity bool -} - -func (a *testTokenAuth) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) { - return map[string]string{"authorization": "Bearer " + a.token}, nil -} - -func (a *testTokenAuth) RequireTransportSecurity() bool { - return a.requireSecurity -} diff --git a/sdk/go/openshell/v1/internal/oidctoken/oidctoken.go b/sdk/go/openshell/v1/internal/oidctoken/oidctoken.go deleted file mode 100644 index 79e7035958..0000000000 --- a/sdk/go/openshell/v1/internal/oidctoken/oidctoken.go +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package oidctoken defines the on-disk OIDC token bundle shared with the -// OpenShell CLI. -package oidctoken - -import ( - "time" - - "golang.org/x/oauth2" -) - -// FileName is the OIDC token bundle in a gateway directory. -const FileName = "oidc_token.json" - -// Bundle is the CLI's on-disk OIDC token bundle (openshell-bootstrap OidcTokenBundle). -type Bundle struct { - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token,omitempty"` - ExpiresAt *uint64 `json:"expires_at,omitempty"` // unix seconds - Issuer string `json:"issuer"` - ClientID string `json:"client_id"` -} - -// FromToken converts tok into the bundle stored for issuer and clientID. -func FromToken(tok *oauth2.Token, issuer, clientID string) Bundle { - b := Bundle{ - AccessToken: tok.AccessToken, - RefreshToken: tok.RefreshToken, - Issuer: issuer, - ClientID: clientID, - } - if !tok.Expiry.IsZero() { - expiresAt := uint64(max(tok.Expiry.Unix(), 0)) - b.ExpiresAt = &expiresAt - } - return b -} - -// Token converts b into a Bearer token. A nil ExpiresAt yields a zero -// Expiry, which oauth2 treats as never expiring. -func (b Bundle) Token() *oauth2.Token { - tok := &oauth2.Token{ - AccessToken: b.AccessToken, - RefreshToken: b.RefreshToken, - TokenType: "Bearer", - } - if b.ExpiresAt != nil { - tok.Expiry = time.Unix(int64(*b.ExpiresAt), 0) - } - return tok -} diff --git a/sdk/go/openshell/v1/internal/options/options.go b/sdk/go/openshell/v1/internal/options/options.go deleted file mode 100644 index f8092cecef..0000000000 --- a/sdk/go/openshell/v1/internal/options/options.go +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package options provides a shared mechanism for applying functional options -// across all SDK entry points. It enforces a single nil-option rule: nil -// entries in an option list are silently ignored. -package options - -// Apply folds opts into target in order, skipping nil entries. -// O is constrained to any named function type whose underlying type is -// func(*T), so callers write options.Apply(&cfg, opts) and both type -// parameters are inferred. -func Apply[T any, O ~func(*T)](target *T, opts []O) { - for _, opt := range opts { - if opt != nil { - opt(target) - } - } -} diff --git a/sdk/go/openshell/v1/internal/options/options_test.go b/sdk/go/openshell/v1/internal/options/options_test.go deleted file mode 100644 index 693326d308..0000000000 --- a/sdk/go/openshell/v1/internal/options/options_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package options - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -// testConfig is a plain config struct for testing Apply. -type testConfig struct { - a int - b string -} - -// testOption is a named option type, mirroring the SDK's pattern. -type testOption func(*testConfig) - -func TestApply_EmptySlice(t *testing.T) { - var cfg testConfig - Apply(&cfg, []testOption{}) - assert.Equal(t, testConfig{}, cfg) -} - -func TestApply_NilSlice(t *testing.T) { - var cfg testConfig - Apply[testConfig, testOption](&cfg, nil) - assert.Equal(t, testConfig{}, cfg) -} - -func TestApply_AllNil(t *testing.T) { - var cfg testConfig - Apply(&cfg, []testOption{nil, nil, nil}) - assert.Equal(t, testConfig{}, cfg) -} - -func TestApply_NilFirst(t *testing.T) { - var cfg testConfig - Apply(&cfg, []testOption{ - nil, - func(c *testConfig) { c.a = 1 }, - }) - assert.Equal(t, 1, cfg.a) -} - -func TestApply_NilMiddle(t *testing.T) { - var cfg testConfig - Apply(&cfg, []testOption{ - func(c *testConfig) { c.a = 1 }, - nil, - func(c *testConfig) { c.b = "two" }, - }) - assert.Equal(t, 1, cfg.a) - assert.Equal(t, "two", cfg.b) -} - -func TestApply_NilLast(t *testing.T) { - var cfg testConfig - Apply(&cfg, []testOption{ - func(c *testConfig) { c.a = 1 }, - nil, - }) - assert.Equal(t, 1, cfg.a) -} - -func TestApply_OrderPreserved(t *testing.T) { - var cfg testConfig - Apply(&cfg, []testOption{ - func(c *testConfig) { c.a = 1 }, - func(c *testConfig) { c.a = 2 }, - func(c *testConfig) { c.a = 3 }, - }) - // The last option wins because options apply in order. - assert.Equal(t, 3, cfg.a) -} - -// liveObject simulates the fake.Client pattern where options mutate a live -// object rather than a plain config struct. -type liveObject struct { - name string - count int -} - -type liveOption func(*liveObject) - -func TestApply_LiveObjectTarget(t *testing.T) { - obj := &liveObject{name: "initial", count: 0} - Apply(obj, []liveOption{ - nil, - func(o *liveObject) { o.name = "updated" }, - nil, - func(o *liveObject) { o.count = 42 }, - }) - assert.Equal(t, "updated", obj.name) - assert.Equal(t, 42, obj.count) -} diff --git a/sdk/go/openshell/v1/logger.go b/sdk/go/openshell/v1/logger.go deleted file mode 100644 index e8274012ae..0000000000 --- a/sdk/go/openshell/v1/logger.go +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// Logger defines structured logging for the SDK. Compatible with logr.Logger -// and slog.Logger adapters. -type Logger = types.Logger diff --git a/sdk/go/openshell/v1/mutations.go b/sdk/go/openshell/v1/mutations.go deleted file mode 100644 index 431010c3e8..0000000000 --- a/sdk/go/openshell/v1/mutations.go +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - -// DeletionOutcome distinguishes completion from asynchronous acceptance. -type DeletionOutcome = types.DeletionOutcome - -// Known deletion outcomes. Unrecognized values do not establish completion. -const ( - DeletionUnspecified = types.DeletionUnspecified - DeletionCompleted = types.DeletionCompleted - DeletionAccepted = types.DeletionAccepted - DeletionAlreadyAbsent = types.DeletionAlreadyAbsent -) - -// DeletionResult describes the original target, not a same-name replacement. -type DeletionResult = types.DeletionResult - -// DeleteOptions configures the missing-target contract. -type DeleteOptions = types.DeleteOptions - -func allowMissing(opts []DeleteOptions) bool { - return len(opts) > 0 && opts[0].AllowMissing -} diff --git a/sdk/go/openshell/v1/oidc/authcode.go b/sdk/go/openshell/v1/oidc/authcode.go deleted file mode 100644 index 0bde13db55..0000000000 --- a/sdk/go/openshell/v1/oidc/authcode.go +++ /dev/null @@ -1,236 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "context" - "crypto/rand" - "crypto/sha256" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "net" - "net/http" - "net/url" - "strings" - "sync" - "time" - - "golang.org/x/oauth2" -) - -// generateCodeVerifier creates a PKCE code verifier per RFC 7636. -// It generates 32 random bytes and encodes them as base64url without -// padding, producing a 43-character string. -func generateCodeVerifier() (string, error) { - b := make([]byte, 32) - if _, err := io.ReadFull(rand.Reader, b); err != nil { - return "", fmt.Errorf("generate code verifier: %w", err) - } - return base64.RawURLEncoding.EncodeToString(b), nil -} - -// codeChallengeS256 computes the S256 PKCE code challenge for the -// given verifier. It returns BASE64URL(SHA256(verifier)) without -// padding, as specified in RFC 7636 Section 4.2. -func codeChallengeS256(verifier string) string { - h := sha256.Sum256([]byte(verifier)) - return base64.RawURLEncoding.EncodeToString(h[:]) -} - -// generateState creates a cryptographic state parameter for the -// authorization request. It generates 16 random bytes encoded as -// base64url without padding. -func generateState() (string, error) { - b := make([]byte, 16) - if _, err := io.ReadFull(rand.Reader, b); err != nil { - return "", fmt.Errorf("generate state: %w", err) - } - return base64.RawURLEncoding.EncodeToString(b), nil -} - -// buildAuthURL constructs the authorization endpoint URL with query -// parameters for the authorization code flow. If challenge is empty, -// PKCE parameters are omitted (for providers that do not support it). -func buildAuthURL(authEndpoint, clientID, redirectURI, state, challenge string, scopes []string) string { - u, err := url.Parse(authEndpoint) - if err != nil || u.Scheme == "" { - u = &url.URL{Path: authEndpoint} - } - q := u.Query() - q.Set("response_type", "code") - q.Set("client_id", clientID) - q.Set("redirect_uri", redirectURI) - q.Set("state", state) - q.Set("scope", strings.Join(scopes, " ")) - - if challenge != "" { - q.Set("code_challenge", challenge) - q.Set("code_challenge_method", "S256") - } - - u.RawQuery = q.Encode() - return u.String() -} - -// callbackResult carries the authorization code (or error) from the -// callback server to the auth code flow orchestrator. -type callbackResult struct { - code string - err error -} - -// startCallbackServer starts a localhost HTTP server to receive the -// OIDC provider's authorization callback. It listens on the specified -// port (use 0 for OS-assigned port). The server handles a single -// callback request and sends the result on the returned channel. -// -// The caller is responsible for calling srv.Close() when done. -func startCallbackServer(ctx context.Context, port int, expectedState string) (*http.Server, <-chan callbackResult, error) { - resultCh := make(chan callbackResult, 1) - var sendOnce sync.Once - send := func(r callbackResult) { - sendOnce.Do(func() { resultCh <- r }) - } - - mux := http.NewServeMux() - mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query() - - if errCode := q.Get("error"); errCode != "" { - desc := q.Get("error_description") - msg := fmt.Sprintf("provider error: %s", errCode) - if desc != "" { - msg += ": " + desc - } - http.Error(w, msg, http.StatusBadRequest) - send(callbackResult{err: fmt.Errorf("%w: %s", ErrAuthCode, msg)}) - return - } - - state := q.Get("state") - if state != expectedState { - http.Error(w, "state mismatch", http.StatusBadRequest) - send(callbackResult{err: fmt.Errorf("%w: state mismatch", ErrAuthCode)}) - return - } - - code := q.Get("code") - if code == "" { - http.Error(w, "missing authorization code", http.StatusBadRequest) - send(callbackResult{err: fmt.Errorf("%w: missing authorization code in callback", ErrAuthCode)}) - return - } - - w.Header().Set("Content-Type", "text/html") - _, _ = fmt.Fprint(w, "

Login successful

You can close this window.

") - send(callbackResult{code: code}) - }) - - listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) - if err != nil { - return nil, nil, fmt.Errorf("%w: failed to start callback server on port %d: %v", ErrCallbackServer, port, err) - } - - srv := &http.Server{ - Addr: listener.Addr().String(), - Handler: mux, - ReadHeaderTimeout: 10 * time.Second, - ReadTimeout: 10 * time.Second, - WriteTimeout: 10 * time.Second, - IdleTimeout: 30 * time.Second, - } - - done := make(chan struct{}) - go func() { - _ = srv.Serve(listener) - close(done) - }() - - go func() { - select { - case <-ctx.Done(): - case <-done: - return - } - shutdownCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - _ = srv.Shutdown(shutdownCtx) - }() - - return srv, resultCh, nil -} - -// tokenResponse is the JSON structure returned by the token endpoint. -type tokenResponse struct { - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` - TokenType string `json:"token_type"` - ExpiresIn int64 `json:"expires_in"` - Error string `json:"error"` - ErrorDesc string `json:"error_description"` -} - -// exchangeCode exchanges an authorization code for tokens at the -// token endpoint. If codeVerifier is empty, the PKCE code_verifier -// parameter is omitted from the request. -// -// Secrets (code, verifier) are never included in error messages. -func exchangeCode(ctx context.Context, tokenEndpoint, clientID, code, redirectURI, codeVerifier string) (*oauth2.Token, error) { - data := url.Values{ - "grant_type": {"authorization_code"}, - "code": {code}, - "client_id": {clientID}, - "redirect_uri": {redirectURI}, - } - if codeVerifier != "" { - data.Set("code_verifier", codeVerifier) - } - - req, err := http.NewRequestWithContext(ctx, http.MethodPost, tokenEndpoint, strings.NewReader(data.Encode())) - if err != nil { - return nil, fmt.Errorf("%w: failed to create token request: %v", ErrAuthCode, err) - } - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - resp, err := oidcHTTPClient.Do(req) - if err != nil { - return nil, fmt.Errorf("%w: token request failed: %v", ErrAuthCode, err) - } - defer func() { _ = resp.Body.Close() }() - - const maxResponseBytes = 1 << 20 - body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes)) - if err != nil { - return nil, fmt.Errorf("%w: failed to read token response: %v", ErrAuthCode, err) - } - - var tokResp tokenResponse - if err := json.Unmarshal(body, &tokResp); err != nil { - return nil, fmt.Errorf("%w: invalid token response JSON: %v", ErrAuthCode, err) - } - - if resp.StatusCode != http.StatusOK || tokResp.Error != "" { - msg := "token exchange failed" - if tokResp.Error != "" { - msg = fmt.Sprintf("token exchange failed: %s", tokResp.Error) - if tokResp.ErrorDesc != "" { - msg += ": " + tokResp.ErrorDesc - } - } - return nil, fmt.Errorf("%w: %s", ErrAuthCode, msg) - } - - tok := &oauth2.Token{ - AccessToken: tokResp.AccessToken, - RefreshToken: tokResp.RefreshToken, - TokenType: tokResp.TokenType, - } - if tokResp.ExpiresIn > 0 { - tok.Expiry = time.Now().Add(time.Duration(tokResp.ExpiresIn) * time.Second) - } - - return tok, nil -} diff --git a/sdk/go/openshell/v1/oidc/authcode_test.go b/sdk/go/openshell/v1/oidc/authcode_test.go deleted file mode 100644 index 5b7d08abd1..0000000000 --- a/sdk/go/openshell/v1/oidc/authcode_test.go +++ /dev/null @@ -1,371 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "context" - "errors" - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// --- T012: PKCE verifier/challenge generation tests --- - -func TestGenerateCodeVerifier_Length(t *testing.T) { - verifier, err := generateCodeVerifier() - require.NoError(t, err) - - // RFC 7636 requires 43-128 characters. Our implementation uses 32 - // random bytes -> 43 base64url characters (no padding). - assert.GreaterOrEqual(t, len(verifier), 43) - assert.LessOrEqual(t, len(verifier), 128) -} - -func TestGenerateCodeVerifier_Base64URLSafe(t *testing.T) { - verifier, err := generateCodeVerifier() - require.NoError(t, err) - - // Must contain only base64url characters (A-Z, a-z, 0-9, -, _). - // No padding (=) allowed per RFC 7636. - for _, c := range verifier { - valid := (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - (c >= '0' && c <= '9') || - c == '-' || c == '_' - assert.True(t, valid, "invalid character in verifier: %c", c) - } -} - -func TestGenerateCodeVerifier_Unique(t *testing.T) { - v1, err := generateCodeVerifier() - require.NoError(t, err) - - v2, err := generateCodeVerifier() - require.NoError(t, err) - - assert.NotEqual(t, v1, v2, "two verifiers should differ (random)") -} - -func TestCodeChallengeS256(t *testing.T) { - // RFC 7636 Appendix B test vector: - // verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" - // challenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" - verifier := "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" - challenge := codeChallengeS256(verifier) - assert.Equal(t, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", challenge) -} - -func TestCodeChallengeS256_NoPadding(t *testing.T) { - verifier, err := generateCodeVerifier() - require.NoError(t, err) - - challenge := codeChallengeS256(verifier) - - // S256 challenge must be base64url without padding. - assert.NotContains(t, challenge, "=") - assert.NotContains(t, challenge, "+") - assert.NotContains(t, challenge, "/") -} - -// --- T013: Auth code flow tests --- - -func TestGenerateState_Length(t *testing.T) { - state, err := generateState() - require.NoError(t, err) - - // 16 random bytes -> 22 base64url chars (no padding). - assert.GreaterOrEqual(t, len(state), 16) -} - -func TestGenerateState_Unique(t *testing.T) { - s1, err := generateState() - require.NoError(t, err) - - s2, err := generateState() - require.NoError(t, err) - - assert.NotEqual(t, s1, s2) -} - -func TestBuildAuthURL(t *testing.T) { - authEndpoint := "https://auth.example.com/authorize" - clientID := "test-client" - redirectURI := "http://localhost:8000/callback" - state := "random-state" - verifier := "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" - challenge := codeChallengeS256(verifier) - scopes := []string{"openid", "profile"} - - authURL := buildAuthURL(authEndpoint, clientID, redirectURI, state, challenge, scopes) - - parsed, err := url.Parse(authURL) - require.NoError(t, err) - - q := parsed.Query() - assert.Equal(t, "code", q.Get("response_type")) - assert.Equal(t, clientID, q.Get("client_id")) - assert.Equal(t, redirectURI, q.Get("redirect_uri")) - assert.Equal(t, state, q.Get("state")) - assert.Equal(t, challenge, q.Get("code_challenge")) - assert.Equal(t, "S256", q.Get("code_challenge_method")) - assert.Equal(t, "openid profile", q.Get("scope")) -} - -func TestBuildAuthURL_NoPKCE(t *testing.T) { - authURL := buildAuthURL( - "https://auth.example.com/authorize", - "test-client", - "http://localhost:8000/callback", - "state", - "", // empty challenge = no PKCE - []string{"openid"}, - ) - - parsed, err := url.Parse(authURL) - require.NoError(t, err) - - q := parsed.Query() - assert.Equal(t, "code", q.Get("response_type")) - assert.Empty(t, q.Get("code_challenge"), "no PKCE when challenge is empty") - assert.Empty(t, q.Get("code_challenge_method")) -} - -func TestStartCallbackServer_ReceivesCode(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - state := "test-state-123" - srv, resultCh, err := startCallbackServer(ctx, 0, state) - require.NoError(t, err) - defer func() { _ = srv.Close() }() - - // Extract the port from the server's listener address. - addr := srv.Addr - callbackURL := fmt.Sprintf("http://%s/callback?code=auth-code-xyz&state=%s", addr, state) - - resp, err := http.Get(callbackURL) - require.NoError(t, err) - _ = resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode) - - result := <-resultCh - require.NoError(t, result.err) - assert.Equal(t, "auth-code-xyz", result.code) -} - -func TestStartCallbackServer_StateMismatch(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - state := "expected-state" - srv, resultCh, err := startCallbackServer(ctx, 0, state) - require.NoError(t, err) - defer func() { _ = srv.Close() }() - - addr := srv.Addr - callbackURL := fmt.Sprintf("http://%s/callback?code=some-code&state=wrong-state", addr) - - resp, err := http.Get(callbackURL) - require.NoError(t, err) - _ = resp.Body.Close() - - result := <-resultCh - require.Error(t, result.err) - assert.True(t, errors.Is(result.err, ErrAuthCode)) - assert.Contains(t, result.err.Error(), "state") -} - -func TestStartCallbackServer_MissingCode(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - state := "test-state" - srv, resultCh, err := startCallbackServer(ctx, 0, state) - require.NoError(t, err) - defer func() { _ = srv.Close() }() - - addr := srv.Addr - callbackURL := fmt.Sprintf("http://%s/callback?state=%s", addr, state) - - resp, err := http.Get(callbackURL) - require.NoError(t, err) - _ = resp.Body.Close() - - result := <-resultCh - require.Error(t, result.err) - assert.True(t, errors.Is(result.err, ErrAuthCode)) -} - -func TestStartCallbackServer_ProviderError(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - state := "test-state" - srv, resultCh, err := startCallbackServer(ctx, 0, state) - require.NoError(t, err) - defer func() { _ = srv.Close() }() - - addr := srv.Addr - callbackURL := fmt.Sprintf("http://%s/callback?error=access_denied&error_description=user+denied&state=%s", addr, state) - - resp, err := http.Get(callbackURL) - require.NoError(t, err) - _ = resp.Body.Close() - - result := <-resultCh - require.Error(t, result.err) - assert.True(t, errors.Is(result.err, ErrAuthCode)) - assert.Contains(t, result.err.Error(), "access_denied") -} - -func TestStartCallbackServer_SpecificPort(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - // Port 0 tells OS to pick a free port. We just verify it works. - srv, _, err := startCallbackServer(ctx, 0, "state") - require.NoError(t, err) - defer func() { _ = srv.Close() }() - - assert.NotEmpty(t, srv.Addr) -} - -func TestExchangeCode_Success(t *testing.T) { - tokenSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - require.Equal(t, http.MethodPost, r.Method) - require.NoError(t, r.ParseForm()) - - assert.Equal(t, "authorization_code", r.Form.Get("grant_type")) - assert.Equal(t, "test-code", r.Form.Get("code")) - assert.Equal(t, "test-client", r.Form.Get("client_id")) - assert.Equal(t, "http://localhost:8000/callback", r.Form.Get("redirect_uri")) - assert.NotEmpty(t, r.Form.Get("code_verifier"), "PKCE verifier should be sent") - - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(`{ - "access_token": "at-123", - "refresh_token": "rt-456", - "token_type": "Bearer", - "expires_in": 3600 - }`)) - })) - defer tokenSrv.Close() - - tok, err := exchangeCode( - context.Background(), - tokenSrv.URL+"/token", - "test-client", - "test-code", - "http://localhost:8000/callback", - "pkce-verifier", - ) - require.NoError(t, err) - assert.Equal(t, "at-123", tok.AccessToken) - assert.Equal(t, "rt-456", tok.RefreshToken) - assert.Equal(t, "Bearer", tok.TokenType) - assert.False(t, tok.Expiry.IsZero()) -} - -func TestExchangeCode_NoPKCE(t *testing.T) { - tokenSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - require.NoError(t, r.ParseForm()) - assert.Empty(t, r.Form.Get("code_verifier"), "no PKCE verifier when empty") - - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(`{ - "access_token": "at-no-pkce", - "token_type": "Bearer", - "expires_in": 3600 - }`)) - })) - defer tokenSrv.Close() - - tok, err := exchangeCode( - context.Background(), - tokenSrv.URL+"/token", - "test-client", - "test-code", - "http://localhost:8000/callback", - "", // empty verifier = no PKCE - ) - require.NoError(t, err) - assert.Equal(t, "at-no-pkce", tok.AccessToken) -} - -func TestExchangeCode_ErrorResponse(t *testing.T) { - tokenSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"error": "invalid_grant", "error_description": "code expired"}`)) - })) - defer tokenSrv.Close() - - _, err := exchangeCode( - context.Background(), - tokenSrv.URL+"/token", - "client", - "bad-code", - "http://localhost/callback", - "verifier", - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrAuthCode)) -} - -func TestExchangeCode_SecretsNotInError(t *testing.T) { - tokenSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"error": "invalid_grant"}`)) - })) - defer tokenSrv.Close() - - _, err := exchangeCode( - context.Background(), - tokenSrv.URL+"/token", - "client", - "secret-code-value", - "http://localhost/callback", - "secret-verifier-value", - ) - require.Error(t, err) - // The error message must not contain the auth code or verifier. - assert.NotContains(t, err.Error(), "secret-code-value") - assert.NotContains(t, err.Error(), "secret-verifier-value") -} - -func TestExchangeCode_InvalidJSON(t *testing.T) { - tokenSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(`not json`)) - })) - defer tokenSrv.Close() - - _, err := exchangeCode( - context.Background(), - tokenSrv.URL+"/token", - "client", - "code", - "http://localhost/callback", - "verifier", - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrAuthCode)) -} - -// tokenResponseJSON is a helper for creating token endpoint responses. -func tokenResponseJSON(accessToken, refreshToken string, expiresIn int) string { - return fmt.Sprintf(`{ - "access_token": %q, - "refresh_token": %q, - "token_type": "Bearer", - "expires_in": %d - }`, accessToken, refreshToken, expiresIn) -} diff --git a/sdk/go/openshell/v1/oidc/browser.go b/sdk/go/openshell/v1/oidc/browser.go deleted file mode 100644 index 6710f3b3da..0000000000 --- a/sdk/go/openshell/v1/oidc/browser.go +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "fmt" - "net/url" - "os/exec" - "runtime" -) - -// browserCommand returns the platform-specific command name and -// arguments for opening a URL in the user's default browser. -func browserCommand(url string) (string, []string) { - return browserCommandForOS(runtime.GOOS, url) -} - -func browserCommandForOS(goos, url string) (string, []string) { - switch goos { - case "darwin": - return "open", []string{url} - case "linux": - return "xdg-open", []string{url} - case "windows": - // Invoke the URL handler directly. Passing an authorization URL to - // cmd.exe would allow '&' and other shell metacharacters in its query - // string to be interpreted as commands. - return "rundll32", []string{"url.dll,FileProtocolHandler", url} - default: - // Fallback: try xdg-open (common on Unix-like systems). - return "xdg-open", []string{url} - } -} - -// openBrowser attempts to open the given URL in the user's default -// browser using the platform-appropriate command. Returns an error if -// the browser could not be launched. -func openBrowser(rawURL string) error { - parsed, err := url.Parse(rawURL) - if err != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") { - return fmt.Errorf("refusing to open non-HTTP URL: %s", rawURL) - } - name, args := browserCommand(rawURL) - return openBrowserWith(name, args...) -} - -// openBrowserWith runs the given command with the provided arguments. -// This is separated from openBrowser to allow testing with arbitrary -// command names. -func openBrowserWith(name string, args ...string) error { - cmd := exec.Command(name, args...) - if err := cmd.Start(); err != nil { - return fmt.Errorf("failed to open browser with %s: %w", name, err) - } - // We don't wait for the browser process to exit. It runs - // independently, and we only care that it launched. - return nil -} diff --git a/sdk/go/openshell/v1/oidc/browser_test.go b/sdk/go/openshell/v1/oidc/browser_test.go deleted file mode 100644 index f2596ea40c..0000000000 --- a/sdk/go/openshell/v1/oidc/browser_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "runtime" - "slices" - "testing" - - "github.com/stretchr/testify/assert" -) - -// T015: Browser opener tests - -func TestBrowserCommand_Platform(t *testing.T) { - name, args := browserCommand("https://example.com/auth") - - switch runtime.GOOS { - case "darwin": - assert.Equal(t, "open", name) - assert.Equal(t, []string{"https://example.com/auth"}, args) - case "linux": - assert.Equal(t, "xdg-open", name) - assert.Equal(t, []string{"https://example.com/auth"}, args) - case "windows": - assert.Equal(t, "rundll32", name) - assert.Equal(t, []string{"url.dll,FileProtocolHandler", "https://example.com/auth"}, args) - default: - // Unknown platform should still return something (even if it fails). - assert.NotEmpty(t, name) - } -} - -func TestBrowserCommand_URLPassedAsArg(t *testing.T) { - testURL := "https://auth.example.com/authorize?client_id=test&state=abc" - _, args := browserCommand(testURL) - - assert.True(t, slices.Contains(args, testURL), "URL should be passed as an argument to the browser command") -} - -func TestBrowserCommandForOS_WindowsDoesNotUseCommandShell(t *testing.T) { - name, args := browserCommandForOS("windows", "https://auth.example.com/authorize?client_id=test&state=abc") - - assert.NotEqual(t, "cmd", name) - assert.NotContains(t, args, "/c") -} - -func TestOpenBrowser_InvalidCommand(t *testing.T) { - // Attempting to open a browser with a non-existent command should - // return an error rather than panic. - err := openBrowserWith("nonexistent-browser-cmd-that-does-not-exist", "https://example.com") - assert.Error(t, err, "should fail when the browser command does not exist") -} diff --git a/sdk/go/openshell/v1/oidc/credentials.go b/sdk/go/openshell/v1/oidc/credentials.go deleted file mode 100644 index 98bfd6219b..0000000000 --- a/sdk/go/openshell/v1/oidc/credentials.go +++ /dev/null @@ -1,186 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "time" - - "golang.org/x/oauth2" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/gateway" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" -) - -// ClientCredentials performs a non-interactive OAuth2 client credentials -// grant (RFC 6749 Section 4.4). It requires [WithIssuer], [WithClientID], -// and [WithClientSecret] (or [WithGateway] combined with [WithClientSecret]). -// -// This flow is intended for service accounts and machine-to-machine -// authentication. No user interaction occurs. The returned token -// typically contains only an access token (no refresh token). -// -// The client secret is never included in error messages (FR-014). -func ClientCredentials(ctx context.Context, opts ...LoginOption) (*oauth2.Token, error) { - cfg, err := resolveClientCredentialsConfig(opts...) - if err != nil { - return nil, err - } - return exchangeClientCredentials(ctx, cfg, false) -} - -func resolveClientCredentialsConfig(opts ...LoginOption) (*loginConfig, error) { - cfg := &loginConfig{} - options.Apply(cfg, opts) - cfg.applyDefaults() - - // Client credentials should not send interactive scopes by default. - // Only send scopes if the caller explicitly set them via WithScopes. - if !cfg.scopesSet { - cfg.scopes = nil - } - - // Resolve OIDC config from gateway if WithGateway was set. - if cfg.gateway != "" { - resolver := cfg.gatewayResolver - if resolver == nil { - resolver = gateway.LoadConfig - } - gwCfg, err := resolver(cfg.gateway) - if err != nil { - return nil, fmt.Errorf("failed to load gateway %q: %w", cfg.gateway, err) - } - if cfg.issuer == "" { - cfg.issuer = gwCfg.OIDCIssuer - } - if cfg.clientID == "" { - cfg.clientID = gwCfg.OIDCClientID - } - if !cfg.audienceSet { - cfg.audience = gwCfg.OIDCAudience - } - if !cfg.scopesSet && gwCfg.OIDCScopes != "" { - cfg.scopes = strings.Fields(gwCfg.OIDCScopes) - cfg.scopesSet = true - } - } - - // Validate required configuration. - if cfg.issuer == "" || cfg.clientID == "" { - return nil, fmt.Errorf( - "%w: issuer and client ID are required (use WithIssuer and WithClientID, or WithGateway)", - ErrOIDCConfig, - ) - } - if cfg.clientSecret == "" && cfg.secretProvider == nil { - return nil, fmt.Errorf( - "%w: client secret is required (use WithClientSecret or WithClientSecretProvider)", - ErrClientCredentials, - ) - } - return cfg, nil -} - -func exchangeClientCredentials(ctx context.Context, cfg *loginConfig, requirePositiveExpiry bool) (*oauth2.Token, error) { - secret := cfg.clientSecret - if cfg.secretProvider != nil { - value, err := cfg.secretProvider(ctx) - if err != nil { - return nil, fmt.Errorf("%w: client secret provider failed", ErrClientCredentials) - } - secret = value - } - if secret == "" { - return nil, fmt.Errorf("%w: client secret must not be empty", ErrClientCredentials) - } - - // Discover provider endpoints. - provider, err := discover(ctx, cfg.issuer) - if err != nil { - return nil, err - } - - // Build the token request with client credentials grant type. - data := url.Values{ - "grant_type": {"client_credentials"}, - "client_id": {cfg.clientID}, - "client_secret": {secret}, - } - if len(cfg.scopes) > 0 { - data.Set("scope", strings.Join(cfg.scopes, " ")) - } - if cfg.audience != "" { - data.Set("audience", cfg.audience) - } - - req, err := http.NewRequestWithContext(ctx, http.MethodPost, provider.TokenEndpoint, strings.NewReader(data.Encode())) - if err != nil { - return nil, fmt.Errorf("%w: failed to create token request", ErrClientCredentials) - } - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - // Use a no-redirect client for token requests that carry client_secret - // in the POST body. A 307/308 redirect would replay the body (including - // the secret) to the redirect target. - noRedirectClient := *oidcHTTPClient - noRedirectClient.CheckRedirect = func(*http.Request, []*http.Request) error { - return http.ErrUseLastResponse - } - resp, err := noRedirectClient.Do(req) - if err != nil { - return nil, fmt.Errorf("%w: token request failed", ErrClientCredentials) - } - defer func() { _ = resp.Body.Close() }() - - const maxResponseBytes = 1 << 20 - body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes+1)) - if err != nil { - return nil, fmt.Errorf("%w: failed to read token response", ErrClientCredentials) - } - if len(body) > maxResponseBytes { - return nil, fmt.Errorf("%w: token response is too large", ErrClientCredentials) - } - - var tokResp tokenResponse - if err := json.Unmarshal(body, &tokResp); err != nil { - return nil, fmt.Errorf("%w: invalid token response JSON", ErrClientCredentials) - } - var fields map[string]json.RawMessage - if err := json.Unmarshal(body, &fields); err != nil { - return nil, fmt.Errorf("%w: invalid token response JSON", ErrClientCredentials) - } - - if resp.StatusCode != http.StatusOK || tokResp.Error != "" { - return nil, fmt.Errorf("%w: provider rejected the exchange (HTTP %d)", ErrClientCredentials, resp.StatusCode) - } - - if tokResp.AccessToken == "" { - return nil, fmt.Errorf("%w: token response missing access_token", ErrClientCredentials) - } - - tok := &oauth2.Token{ - AccessToken: tokResp.AccessToken, - RefreshToken: tokResp.RefreshToken, - TokenType: tokResp.TokenType, - } - _, expiresInPresent := fields["expires_in"] - if expiresInPresent && tokResp.ExpiresIn <= 0 { - return nil, fmt.Errorf("%w: token response requires a positive expires_in", ErrClientCredentials) - } - if !expiresInPresent { - if requirePositiveExpiry { - return nil, fmt.Errorf("%w: token response requires a positive expires_in", ErrClientCredentials) - } - return tok, nil - } - tok.Expiry = time.Now().Add(time.Duration(tokResp.ExpiresIn) * time.Second) - - return tok, nil -} diff --git a/sdk/go/openshell/v1/oidc/credentials_auth.go b/sdk/go/openshell/v1/oidc/credentials_auth.go deleted file mode 100644 index 24012c8bf4..0000000000 --- a/sdk/go/openshell/v1/oidc/credentials_auth.go +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "context" - "sync" - "time" - - "golang.org/x/oauth2" - "golang.org/x/sync/singleflight" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -const clientCredentialsLeeway = 30 * time.Second - -type clientCredentialsAuth struct { - cfg *loginConfig - mu sync.Mutex - token *oauth2.Token - group singleflight.Group -} - -// NewClientCredentialsAuth returns a renewable, memory-only client-credentials -// AuthProvider suitable for v1.Config.Auth or gateway.WithAuth. Acquisition is -// lazy, concurrent RPCs share one exchange, and expired tokens are never used -// when renewal fails. -func NewClientCredentialsAuth(opts ...LoginOption) (types.AuthProvider, error) { - cfg, err := resolveClientCredentialsConfig(opts...) - if err != nil { - return nil, err - } - return &clientCredentialsAuth{cfg: cfg}, nil -} - -func (a *clientCredentialsAuth) GetRequestMetadata(ctx context.Context, _ ...string) (map[string]string, error) { - if accessToken, ok := a.cachedAccessToken(); ok { - return map[string]string{"authorization": "Bearer " + accessToken}, nil - } - - result := a.group.DoChan("exchange", func() (any, error) { - return a.accessTokenForExchange() - }) - select { - case <-ctx.Done(): - return nil, ctx.Err() - case exchange := <-result: - if exchange.Err != nil { - return nil, exchange.Err - } - return map[string]string{"authorization": "Bearer " + exchange.Val.(string)}, nil - } -} - -func (a *clientCredentialsAuth) cachedAccessToken() (string, bool) { - a.mu.Lock() - defer a.mu.Unlock() - if a.token != nil && time.Now().Add(clientCredentialsLeeway).Before(a.token.Expiry) { - return a.token.AccessToken, true - } - return "", false -} - -func (a *clientCredentialsAuth) accessTokenForExchange() (string, error) { - // A caller can miss the initial cache check, then become the next flight - // leader after another exchange completes and singleflight removes it. - // Re-check here so that caller reuses the token instead of exchanging again. - if accessToken, ok := a.cachedAccessToken(); ok { - return accessToken, nil - } - - exchangeCtx, cancel := context.WithTimeout(context.Background(), a.cfg.timeout) - defer cancel() - token, err := exchangeClientCredentials(exchangeCtx, a.cfg, true) - if err != nil { - return "", err - } - a.mu.Lock() - a.token = token - a.mu.Unlock() - return token.AccessToken, nil -} - -func (*clientCredentialsAuth) RequireTransportSecurity() bool { return true } - -func (*clientCredentialsAuth) String() string { return "oidc.ClientCredentialsAuth" } - -func (*clientCredentialsAuth) GoString() string { return "oidc.ClientCredentialsAuth{}" } diff --git a/sdk/go/openshell/v1/oidc/credentials_test.go b/sdk/go/openshell/v1/oidc/credentials_test.go deleted file mode 100644 index 3269c44d40..0000000000 --- a/sdk/go/openshell/v1/oidc/credentials_test.go +++ /dev/null @@ -1,662 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "os" - "path/filepath" - "sync" - "sync/atomic" - "testing" - "time" - - "golang.org/x/oauth2" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/gateway" -) - -type clientCredentialsFixture struct { - Request struct { - GrantType string `json:"grant_type"` - ClientID string `json:"client_id"` - ClientSecret string `json:"client_secret"` - Scopes []string `json:"scopes"` - Scope string `json:"scope"` - Audience string `json:"audience"` - } `json:"request"` - Expiry struct { - LeewaySeconds int `json:"leeway_seconds"` - ValidExpiresIn int64 `json:"valid_expires_in"` - InvalidExpiresIn []json.RawMessage `json:"invalid_expires_in"` - } `json:"expiry"` - Discovery struct { - ConfiguredIssuer string `json:"configured_issuer"` - MatchingIssuer string `json:"matching_issuer"` - MismatchedIssuer string `json:"mismatched_issuer"` - RedirectStatuses []int `json:"redirect_statuses"` - } `json:"discovery"` - Limits struct { - MaxResponseBytes int `json:"max_response_bytes"` - } `json:"limits"` - URLs struct { - Allowed []string `json:"allowed"` - Rejected []string `json:"rejected"` - } `json:"urls"` -} - -func loadClientCredentialsFixture(t *testing.T) clientCredentialsFixture { - t.Helper() - data, err := os.ReadFile(filepath.Join("..", "..", "..", "..", "conformance", "oauth-client-credentials.json")) - require.NoError(t, err) - var fixture clientCredentialsFixture - require.NoError(t, json.Unmarshal(data, &fixture)) - return fixture -} - -func TestClientCredentialsConformanceFixture(t *testing.T) { - fixture := loadClientCredentialsFixture(t) - assert.Equal(t, int(clientCredentialsLeeway/time.Second), fixture.Expiry.LeewaySeconds) - for _, raw := range fixture.URLs.Allowed { - assert.NoError(t, validateSecureURL("issuer", raw), raw) - } - for _, raw := range fixture.URLs.Rejected { - assert.Error(t, validateSecureURL("issuer", raw), raw) - } -} - -func TestClientCredentialsRejectsInvalidExpiryConformance(t *testing.T) { - fixture := loadClientCredentialsFixture(t) - for _, raw := range fixture.Expiry.InvalidExpiresIn { - raw := raw - t.Run(string(raw), func(t *testing.T) { - resetDiscoveryCache() - var server *httptest.Server - server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/.well-known/openid-configuration" { - _ = json.NewEncoder(w).Encode(map[string]string{ - "issuer": server.URL, - "authorization_endpoint": server.URL + "/authorize", - "token_endpoint": server.URL + "/token", - }) - return - } - _, _ = fmt.Fprintf(w, `{"access_token":"sensitive-access-token","expires_in":%s}`, raw) - })) - t.Cleanup(server.Close) - - _, err := ClientCredentials( - context.Background(), - WithIssuer(server.URL), - WithClientID("client"), - WithClientSecret("secret"), - ) - require.Error(t, err) - assert.NotContains(t, err.Error(), "sensitive-access-token") - }) - } -} - -func TestClientCredentialsMissingExpiryCompatibility(t *testing.T) { - resetDiscoveryCache() - var server *httptest.Server - server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/.well-known/openid-configuration" { - _ = json.NewEncoder(w).Encode(map[string]string{ - "issuer": server.URL, - "authorization_endpoint": server.URL + "/authorize", - "token_endpoint": server.URL + "/token", - }) - return - } - _, _ = w.Write([]byte(`{"access_token":"token","token_type":"Bearer"}`)) - })) - t.Cleanup(server.Close) - - opts := []LoginOption{ - WithIssuer(server.URL), - WithClientID("client"), - WithClientSecret("secret"), - } - token, err := ClientCredentials(context.Background(), opts...) - require.NoError(t, err) - assert.True(t, token.Expiry.IsZero()) - - auth, err := NewClientCredentialsAuth(opts...) - require.NoError(t, err) - metadata, err := auth.GetRequestMetadata(context.Background()) - require.Error(t, err) - assert.Nil(t, metadata) - assert.Contains(t, err.Error(), "positive expires_in") -} - -func TestClientCredentialsRefusesRedirectsConformance(t *testing.T) { - fixture := loadClientCredentialsFixture(t) - for _, status := range fixture.Discovery.RedirectStatuses { - status := status - for _, endpoint := range []string{"discovery", "token"} { - endpoint := endpoint - t.Run(fmt.Sprintf("%s_%d", endpoint, status), func(t *testing.T) { - resetDiscoveryCache() - var server *httptest.Server - var redirected atomic.Bool - server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/.well-known/openid-configuration": - if endpoint == "discovery" { - w.Header().Set("Location", server.URL+"/redirected") - w.WriteHeader(status) - return - } - _ = json.NewEncoder(w).Encode(map[string]string{ - "issuer": server.URL, - "authorization_endpoint": server.URL + "/authorize", - "token_endpoint": server.URL + "/token", - }) - case "/token": - w.Header().Set("Location", server.URL+"/redirected") - w.WriteHeader(status) - case "/redirected": - redirected.Store(true) - } - })) - t.Cleanup(server.Close) - - _, err := ClientCredentials( - context.Background(), - WithIssuer(server.URL), - WithClientID("client"), - WithClientSecret("secret"), - ) - require.Error(t, err) - assert.False(t, redirected.Load()) - }) - } - } -} - -func TestClientCredentialsRejectsMismatchedIssuerConformance(t *testing.T) { - resetDiscoveryCache() - fixture := loadClientCredentialsFixture(t) - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - _ = json.NewEncoder(w).Encode(map[string]string{ - "issuer": fixture.Discovery.MismatchedIssuer, - "authorization_endpoint": fixture.Discovery.MismatchedIssuer + "/authorize", - "token_endpoint": fixture.Discovery.MismatchedIssuer + "/token", - }) - })) - t.Cleanup(server.Close) - - _, err := ClientCredentials( - context.Background(), - WithIssuer(server.URL), - WithClientID("client"), - WithClientSecret("secret"), - ) - require.Error(t, err) - assert.Contains(t, err.Error(), "issuer") -} - -func TestClientCredentialsRejectsOversizedResponseConformance(t *testing.T) { - resetDiscoveryCache() - fixture := loadClientCredentialsFixture(t) - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - _, _ = w.Write(make([]byte, fixture.Limits.MaxResponseBytes+1)) - })) - t.Cleanup(server.Close) - - _, err := ClientCredentials( - context.Background(), - WithIssuer(server.URL), - WithClientID("client"), - WithClientSecret("secret"), - ) - require.Error(t, err) - assert.Contains(t, err.Error(), "too large") -} - -func TestClientCredentialsAuthSingleFlightRenewalAndFields(t *testing.T) { - resetDiscoveryCache() - fixture := loadClientCredentialsFixture(t) - var server *httptest.Server - var calls atomic.Int32 - var formsMu sync.Mutex - var forms []url.Values - release := make(chan struct{}) - server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/.well-known/openid-configuration" { - _ = json.NewEncoder(w).Encode(map[string]string{ - "issuer": server.URL, - "authorization_endpoint": server.URL + "/authorize", - "token_endpoint": server.URL + "/token", - }) - return - } - if calls.Add(1) == 1 { - <-release - } - require.NoError(t, r.ParseForm()) - formsMu.Lock() - forms = append(forms, r.Form) - formsMu.Unlock() - _, _ = w.Write([]byte(tokenResponseJSON("token", "", 3600))) - })) - t.Cleanup(server.Close) - - auth, err := NewClientCredentialsAuth( - WithIssuer(server.URL), - WithClientID(fixture.Request.ClientID), - WithClientSecret(fixture.Request.ClientSecret), - WithScopes(fixture.Request.Scopes...), - WithAudience(fixture.Request.Audience), - ) - require.NoError(t, err) - assert.NotContains(t, fmt.Sprintf("%#v", auth), "secret") - - results := make(chan error, 12) - for range 12 { - go func() { - metadata, callErr := auth.GetRequestMetadata(context.Background()) - if callErr == nil && metadata["authorization"] != "Bearer token" { - callErr = errors.New("unexpected authorization metadata") - } - results <- callErr - }() - } - require.Eventually(t, func() bool { return calls.Load() == 1 }, time.Second, time.Millisecond) - close(release) - for range 12 { - require.NoError(t, <-results) - } - assert.Equal(t, int32(1), calls.Load()) - require.Len(t, forms, 1) - assert.Equal(t, fixture.Request.GrantType, forms[0].Get("grant_type")) - assert.Equal(t, fixture.Request.ClientID, forms[0].Get("client_id")) - assert.Equal(t, fixture.Request.ClientSecret, forms[0].Get("client_secret")) - assert.Equal(t, fixture.Request.Scope, forms[0].Get("scope")) - assert.Equal(t, fixture.Request.Audience, forms[0].Get("audience")) - - concrete := auth.(*clientCredentialsAuth) - concrete.mu.Lock() - concrete.token.Expiry = time.Now() - concrete.mu.Unlock() - _, err = auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - assert.Equal(t, int32(2), calls.Load()) -} - -func TestClientCredentialsAuthLateFlightReusesCachedToken(t *testing.T) { - auth := &clientCredentialsAuth{ - token: &oauth2.Token{ - AccessToken: "cached-token", - Expiry: time.Now().Add(time.Hour), - }, - } - - accessToken, err := auth.accessTokenForExchange() - require.NoError(t, err) - assert.Equal(t, "cached-token", accessToken) -} - -func TestClientCredentialsAuthCancellationDoesNotPoisonSharedExchange(t *testing.T) { - resetDiscoveryCache() - var server *httptest.Server - started := make(chan struct{}) - release := make(chan struct{}) - server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/.well-known/openid-configuration" { - _ = json.NewEncoder(w).Encode(map[string]string{ - "issuer": server.URL, - "authorization_endpoint": server.URL + "/authorize", - "token_endpoint": server.URL + "/token", - }) - return - } - close(started) - <-release - _, _ = w.Write([]byte(tokenResponseJSON("token", "", 3600))) - })) - t.Cleanup(server.Close) - auth, err := NewClientCredentialsAuth( - WithIssuer(server.URL), WithClientID("client"), WithClientSecret("secret"), - ) - require.NoError(t, err) - - canceled, cancel := context.WithCancel(context.Background()) - first := make(chan error, 1) - go func() { - _, callErr := auth.GetRequestMetadata(canceled) - first <- callErr - }() - <-started - second := make(chan error, 1) - go func() { - _, callErr := auth.GetRequestMetadata(context.Background()) - second <- callErr - }() - cancel() - assert.ErrorIs(t, <-first, context.Canceled) - close(release) - require.NoError(t, <-second) -} - -func TestClientCredentialsAuthRedactsSecretProviderError(t *testing.T) { - auth, err := NewClientCredentialsAuth( - WithIssuer("http://localhost:8080"), - WithClientID("client"), - WithClientSecretProvider(func(context.Context) (string, error) { - return "", errors.New("supplier-sensitive-detail") - }), - ) - require.NoError(t, err) - _, err = auth.GetRequestMetadata(context.Background()) - require.Error(t, err) - assert.NotContains(t, err.Error(), "supplier-sensitive-detail") -} - -func TestClientCredentialsAuthFailsClosedOnRenewalError(t *testing.T) { - resetDiscoveryCache() - var server *httptest.Server - var fail atomic.Bool - server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/.well-known/openid-configuration" { - _ = json.NewEncoder(w).Encode(map[string]string{ - "issuer": server.URL, - "authorization_endpoint": server.URL + "/authorize", - "token_endpoint": server.URL + "/token", - }) - return - } - if fail.Load() { - w.WriteHeader(http.StatusServiceUnavailable) - _, _ = w.Write([]byte(`{"error":"provider-sensitive-detail"}`)) - return - } - _, _ = w.Write([]byte(tokenResponseJSON("stale", "", 3600))) - })) - t.Cleanup(server.Close) - auth, err := NewClientCredentialsAuth( - WithIssuer(server.URL), WithClientID("client"), WithClientSecret("secret"), - ) - require.NoError(t, err) - _, err = auth.GetRequestMetadata(context.Background()) - require.NoError(t, err) - concrete := auth.(*clientCredentialsAuth) - concrete.mu.Lock() - concrete.token.Expiry = time.Now() - concrete.mu.Unlock() - fail.Store(true) - metadata, err := auth.GetRequestMetadata(context.Background()) - require.Error(t, err) - assert.Nil(t, metadata) - assert.NotContains(t, err.Error(), "stale") - assert.NotContains(t, err.Error(), "provider-sensitive-detail") -} - -// --- T023: Client credentials tests --- - -// setupCredentialsMockProvider creates a mock OIDC provider for client -// credentials testing. The token endpoint validates Basic Auth and -// returns a token response. Returns the server and the expected -// client ID / client secret pair. -func setupCredentialsMockProvider(t *testing.T, expectedClientID, expectedSecret string) *httptest.Server { - t.Helper() - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "authorization_endpoint": srv.URL + "/authorize", - "token_endpoint": srv.URL + "/token", - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { - _ = r.ParseForm() - - // Validate grant type. - if r.Form.Get("grant_type") != "client_credentials" { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"error":"unsupported_grant_type","error_description":"expected client_credentials"}`)) - return - } - - // Check credentials from form body (client_id + client_secret) - // or Basic Auth header. - clientID := r.Form.Get("client_id") - clientSecret := r.Form.Get("client_secret") - if clientID == "" || clientSecret == "" { - // Try Basic Auth. - var ok bool - clientID, clientSecret, ok = r.BasicAuth() - if !ok { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusUnauthorized) - _, _ = w.Write([]byte(`{"error":"invalid_client","error_description":"missing credentials"}`)) - return - } - } - - if clientID != expectedClientID || clientSecret != expectedSecret { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusUnauthorized) - _, _ = w.Write([]byte(`{"error":"invalid_client","error_description":"invalid credentials"}`)) - return - } - - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(tokenResponseJSON("cc-access-token", "", 3600))) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - return srv -} - -// TestClientCredentials_Success verifies the happy path: valid client -// ID, secret, and issuer produce a valid access token. -func TestClientCredentials_Success(t *testing.T) { - resetDiscoveryCache() - - provider := setupCredentialsMockProvider(t, "my-client", "my-secret") - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - tok, err := ClientCredentials(ctx, - WithIssuer(provider.URL), - WithClientID("my-client"), - WithClientSecret("my-secret"), - ) - require.NoError(t, err) - assert.Equal(t, "cc-access-token", tok.AccessToken) - assert.Empty(t, tok.RefreshToken, "client credentials should not return a refresh token") -} - -// TestClientCredentials_MissingIssuer verifies that ClientCredentials -// returns ErrOIDCConfig when the issuer is not set. -func TestClientCredentials_MissingIssuer(t *testing.T) { - resetDiscoveryCache() - - _, err := ClientCredentials(context.Background(), - WithClientID("my-client"), - WithClientSecret("my-secret"), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrOIDCConfig), "expected ErrOIDCConfig, got: %v", err) -} - -// TestClientCredentials_MissingClientID verifies that ClientCredentials -// returns ErrOIDCConfig when the client ID is not set. -func TestClientCredentials_MissingClientID(t *testing.T) { - resetDiscoveryCache() - - _, err := ClientCredentials(context.Background(), - WithIssuer("https://example.com"), - WithClientSecret("my-secret"), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrOIDCConfig), "expected ErrOIDCConfig, got: %v", err) -} - -// TestClientCredentials_MissingClientSecret verifies that -// ClientCredentials returns ErrClientCredentials when the secret is -// missing. -func TestClientCredentials_MissingClientSecret(t *testing.T) { - resetDiscoveryCache() - - _, err := ClientCredentials(context.Background(), - WithIssuer("https://example.com"), - WithClientID("my-client"), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrClientCredentials), "expected ErrClientCredentials, got: %v", err) -} - -// TestClientCredentials_InvalidCredentials verifies that -// ClientCredentials returns ErrClientCredentials when the provider -// rejects the credentials, and that the secret is not leaked in the -// error message. -func TestClientCredentials_InvalidCredentials(t *testing.T) { - resetDiscoveryCache() - - provider := setupCredentialsMockProvider(t, "good-client", "good-secret") - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - _, err := ClientCredentials(ctx, - WithIssuer(provider.URL), - WithClientID("good-client"), - WithClientSecret("wrong-secret"), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrClientCredentials), "expected ErrClientCredentials, got: %v", err) - - // FR-014: The secret must NEVER appear in error messages. - assert.NotContains(t, err.Error(), "wrong-secret", "secret must not leak in error message") - assert.NotContains(t, err.Error(), "good-secret", "secret must not leak in error message") -} - -// TestClientCredentials_DiscoveryFailure verifies that -// ClientCredentials returns ErrDiscovery when the provider is -// unreachable. -func TestClientCredentials_DiscoveryFailure(t *testing.T) { - resetDiscoveryCache() - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - _, err := ClientCredentials(ctx, - WithIssuer("http://127.0.0.1:1"), - WithClientID("my-client"), - WithClientSecret("my-secret"), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDiscovery), "expected ErrDiscovery, got: %v", err) -} - -// TestClientCredentials_WithGateway verifies that ClientCredentials -// resolves OIDC config from gateway metadata when WithGateway is set. -func TestClientCredentials_WithGateway(t *testing.T) { - resetDiscoveryCache() - - provider := setupCredentialsMockProvider(t, "gw-client", "gw-secret") - - fakeConfig := &gateway.Config{ - Name: "cc-gateway", - Endpoint: "gateway.example.com:443", - Dir: t.TempDir(), - OIDCIssuer: provider.URL, - OIDCClientID: "gw-client", - } - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - tok, err := ClientCredentials(ctx, - WithGateway("cc-gateway"), - WithClientSecret("gw-secret"), - withGatewayResolver(func(name string) (*gateway.Config, error) { - assert.Equal(t, "cc-gateway", name) - return fakeConfig, nil - }), - ) - require.NoError(t, err) - assert.Equal(t, "cc-access-token", tok.AccessToken) -} - -// TestClientCredentials_CustomScopes verifies that WithScopes overrides -// default scopes in the client credentials request. -func TestClientCredentials_CustomScopes(t *testing.T) { - resetDiscoveryCache() - - var receivedScope string - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "authorization_endpoint": srv.URL + "/authorize", - "token_endpoint": srv.URL + "/token", - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { - _ = r.ParseForm() - receivedScope = r.Form.Get("scope") - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(tokenResponseJSON("scoped-cc-token", "", 3600))) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - tok, err := ClientCredentials(ctx, - WithIssuer(srv.URL), - WithClientID("my-client"), - WithClientSecret("my-secret"), - WithScopes("api:read", "api:write"), - ) - require.NoError(t, err) - assert.Equal(t, "scoped-cc-token", tok.AccessToken) - assert.Equal(t, "api:read api:write", receivedScope) -} - -// TestClientCredentials_ContextCancellation verifies that -// ClientCredentials respects context cancellation. -func TestClientCredentials_ContextCancellation(t *testing.T) { - resetDiscoveryCache() - - provider := setupCredentialsMockProvider(t, "my-client", "my-secret") - - ctx, cancel := context.WithCancel(context.Background()) - cancel() // cancel immediately - - _, err := ClientCredentials(ctx, - WithIssuer(provider.URL), - WithClientID("my-client"), - WithClientSecret("my-secret"), - ) - require.Error(t, err) -} diff --git a/sdk/go/openshell/v1/oidc/device.go b/sdk/go/openshell/v1/oidc/device.go deleted file mode 100644 index 049d053d71..0000000000 --- a/sdk/go/openshell/v1/oidc/device.go +++ /dev/null @@ -1,291 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "time" - - "golang.org/x/oauth2" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/gateway" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" -) - -// deviceAuthResponse holds the parsed response from the device -// authorization endpoint (RFC 8628 Section 3.2). -type deviceAuthResponse struct { - DeviceCode string `json:"device_code"` - UserCode string `json:"user_code"` - VerificationURI string `json:"verification_uri"` - VerificationURIComplete string `json:"verification_uri_complete"` - ExpiresIn int64 `json:"expires_in"` - Interval int64 `json:"interval"` -} - -// DeviceLogin performs an OAuth2 device authorization grant (RFC 8628). -// -// The flow requests a device code and user code from the provider's -// device authorization endpoint, displays them to the user (via -// [WithDisplayFunc] or stdout), and polls the token endpoint until the -// user completes authorization. -// -// Required options: [WithIssuer] and [WithClientID], or [WithGateway]. -// -// The polling loop respects the provider's interval and handles the -// following token endpoint error codes: -// - "authorization_pending": continue polling at the current interval -// - "slow_down": increase the polling interval by 5 seconds (RFC 8628 Section 3.5) -// - "expired_token": the device code has expired, return [ErrDeviceCode] -// - any other error: return [ErrDeviceCode] -func DeviceLogin(ctx context.Context, opts ...LoginOption) (*oauth2.Token, error) { - cfg := &loginConfig{} - options.Apply(cfg, opts) - cfg.applyDefaults() - if _, hasDeadline := ctx.Deadline(); !hasDeadline && cfg.timeout > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, cfg.timeout) - defer cancel() - } - - // Resolve OIDC config from gateway if WithGateway was set. - if cfg.gateway != "" { - resolver := cfg.gatewayResolver - if resolver == nil { - resolver = gateway.LoadConfig - } - gwCfg, err := resolver(cfg.gateway) - if err != nil { - return nil, fmt.Errorf("failed to load gateway %q: %w", cfg.gateway, err) - } - if gwCfg.OIDCIssuer == "" || gwCfg.OIDCClientID == "" { - return nil, fmt.Errorf( - "%w: gateway %q has no OIDC configuration (missing oidc_issuer or oidc_client_id in metadata.json)", - ErrOIDCConfig, cfg.gateway, - ) - } - cfg.issuer = gwCfg.OIDCIssuer - cfg.clientID = gwCfg.OIDCClientID - } - - // Validate required configuration. - if cfg.issuer == "" || cfg.clientID == "" { - return nil, fmt.Errorf( - "%w: issuer and client ID are required (use WithIssuer and WithClientID, or WithGateway)", - ErrOIDCConfig, - ) - } - - // Discover provider endpoints. - provider, err := discover(ctx, cfg.issuer) - if err != nil { - return nil, err - } - - // Verify the provider supports device authorization. - if provider.DeviceAuthorizationEndpoint == "" { - return nil, fmt.Errorf( - "%w: provider does not support device authorization (no device_authorization_endpoint in discovery)", - ErrDeviceCode, - ) - } - - // Request a device code from the provider. - deviceResp, err := requestDeviceCode(ctx, provider.DeviceAuthorizationEndpoint, cfg.clientID, cfg.scopes) - if err != nil { - return nil, err - } - - // Display the verification URL and user code to the user. - if cfg.displayFunc != nil { - cfg.displayFunc(deviceResp.VerificationURI, deviceResp.UserCode) - } else { - fmt.Printf("To sign in, visit: %s\n", deviceResp.VerificationURI) - fmt.Printf("Enter code: %s\n", deviceResp.UserCode) - } - - // Enforce device code lifetime from the provider's expires_in field. - // If the caller's context already has a shorter deadline, that takes - // precedence. This prevents indefinite polling against non-compliant - // providers that never return expired_token. - if deviceResp.ExpiresIn > 0 { - expiry := time.Duration(deviceResp.ExpiresIn) * time.Second - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, expiry) - defer cancel() - } - - // Poll the token endpoint until authorization completes, expires, - // or the context is cancelled. - interval := deviceResp.Interval - if interval < 1 { - interval = 5 // default polling interval per RFC 8628 - } - - return pollDeviceToken(ctx, provider.TokenEndpoint, cfg.clientID, deviceResp.DeviceCode, interval) -} - -// requestDeviceCode sends a POST to the device authorization endpoint -// and returns the parsed response containing the device code, user -// code, and verification URI. -func requestDeviceCode(ctx context.Context, endpoint, clientID string, scopes []string) (*deviceAuthResponse, error) { - data := url.Values{ - "client_id": {clientID}, - } - if len(scopes) > 0 { - data.Set("scope", strings.Join(scopes, " ")) - } - - req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, strings.NewReader(data.Encode())) - if err != nil { - return nil, fmt.Errorf("%w: failed to create device authorization request", ErrDeviceCode) - } - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - resp, err := oidcHTTPClient.Do(req) - if err != nil { - return nil, fmt.Errorf("%w: device authorization request failed", ErrDeviceCode) - } - defer func() { _ = resp.Body.Close() }() - - const maxResponseBytes = 1 << 20 - body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes)) - if err != nil { - return nil, fmt.Errorf("%w: failed to read device authorization response", ErrDeviceCode) - } - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%w: device authorization endpoint returned HTTP %d", ErrDeviceCode, resp.StatusCode) - } - - var deviceResp deviceAuthResponse - if err := json.Unmarshal(body, &deviceResp); err != nil { - return nil, fmt.Errorf("%w: invalid device authorization response JSON", ErrDeviceCode) - } - - if deviceResp.DeviceCode == "" || deviceResp.UserCode == "" { - return nil, fmt.Errorf("%w: device authorization response missing device_code or user_code", ErrDeviceCode) - } - - return &deviceResp, nil -} - -// pollDeviceToken polls the token endpoint at the given interval until -// the user completes authorization. It handles RFC 8628 error codes: -// - "authorization_pending": keep polling -// - "slow_down": increase interval by 5 seconds -// - "expired_token": return ErrDeviceCode -func pollDeviceToken(ctx context.Context, tokenEndpoint, clientID, deviceCode string, interval int64) (*oauth2.Token, error) { - ticker := time.NewTicker(time.Duration(interval) * time.Second) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return nil, fmt.Errorf("%w: %v", ErrTimeout, ctx.Err()) - case <-ticker.C: - tok, done, slowDown, err := tryDeviceTokenExchange(ctx, tokenEndpoint, clientID, deviceCode) - if done { - if err != nil { - return nil, err - } - return tok, nil - } - // Adjust interval if the provider requested slow_down - // (+5 seconds per RFC 8628 Section 3.5). - if slowDown < 0 { - interval += 5 - ticker.Reset(time.Duration(interval) * time.Second) - } - } - } -} - -// tryDeviceTokenExchange makes a single token request for the device -// code grant. Returns: -// - (token, true, 0, nil): success -// - (nil, true, 0, err): terminal error (expired, access_denied, etc.) -// - (nil, false, interval, nil): continue polling (authorization_pending or slow_down) -func tryDeviceTokenExchange(ctx context.Context, tokenEndpoint, clientID, deviceCode string) (*oauth2.Token, bool, int64, error) { - data := url.Values{ - "grant_type": {"urn:ietf:params:oauth:grant-type:device_code"}, - "device_code": {deviceCode}, - "client_id": {clientID}, - } - - req, err := http.NewRequestWithContext(ctx, http.MethodPost, tokenEndpoint, strings.NewReader(data.Encode())) - if err != nil { - return nil, true, 0, fmt.Errorf("%w: failed to create token request", ErrDeviceCode) - } - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - resp, err := oidcHTTPClient.Do(req) - if err != nil { - // If the context was cancelled or timed out, surface that as - // ErrTimeout so callers can distinguish "user/caller cancelled" - // from a genuine device-code error. - if ctx.Err() != nil { - return nil, true, 0, fmt.Errorf("%w: %v", ErrTimeout, ctx.Err()) - } - // Other network errors during polling are terminal. - return nil, true, 0, fmt.Errorf("%w: token request failed", ErrDeviceCode) - } - defer func() { _ = resp.Body.Close() }() - - const maxTokenResponseBytes = 1 << 20 - body, err := io.ReadAll(io.LimitReader(resp.Body, maxTokenResponseBytes)) - if err != nil { - return nil, true, 0, fmt.Errorf("%w: failed to read token response", ErrDeviceCode) - } - - var tokResp tokenResponse - if err := json.Unmarshal(body, &tokResp); err != nil { - return nil, true, 0, fmt.Errorf("%w: invalid token response JSON", ErrDeviceCode) - } - - // Handle error responses per RFC 8628 Section 3.5. - if tokResp.Error != "" { - switch tokResp.Error { - case "authorization_pending": - // User has not yet completed authorization. Keep polling. - return nil, false, 0, nil - case "slow_down": - // Provider requests increased interval (+5 seconds per RFC 8628). - // Return a sentinel value; the caller adds 5 to current interval. - return nil, false, -1, nil - case "expired_token": - return nil, true, 0, fmt.Errorf("%w: device code expired", ErrDeviceCode) - case "access_denied": - return nil, true, 0, fmt.Errorf("%w: access denied by user", ErrDeviceCode) - default: - msg := fmt.Sprintf("device code exchange failed: %s", tokResp.Error) - if tokResp.ErrorDesc != "" { - msg += ": " + tokResp.ErrorDesc - } - return nil, true, 0, fmt.Errorf("%w: %s", ErrDeviceCode, msg) - } - } - - // Success: parse the token. - if resp.StatusCode != http.StatusOK { - return nil, true, 0, fmt.Errorf("%w: token endpoint returned HTTP %d", ErrDeviceCode, resp.StatusCode) - } - - tok := &oauth2.Token{ - AccessToken: tokResp.AccessToken, - RefreshToken: tokResp.RefreshToken, - TokenType: tokResp.TokenType, - } - if tokResp.ExpiresIn > 0 { - tok.Expiry = time.Now().Add(time.Duration(tokResp.ExpiresIn) * time.Second) - } - - return tok, true, 0, nil -} diff --git a/sdk/go/openshell/v1/oidc/device_test.go b/sdk/go/openshell/v1/oidc/device_test.go deleted file mode 100644 index 7571de6b82..0000000000 --- a/sdk/go/openshell/v1/oidc/device_test.go +++ /dev/null @@ -1,698 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "context" - "encoding/json" - "errors" - "net/http" - "net/http/httptest" - "sync/atomic" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/gateway" -) - -// --- T025: Device code flow tests --- - -// setupDeviceMockProvider creates a mock OIDC provider for device code -// flow testing. The device authorization endpoint returns a device code -// and verification URL. The token endpoint simulates polling behavior: -// it returns "authorization_pending" for the first N polls, then returns -// a valid token response. -func setupDeviceMockProvider(t *testing.T, pendingPolls int) *httptest.Server { - t.Helper() - mux := http.NewServeMux() - var srv *httptest.Server - var pollCount atomic.Int32 - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "authorization_endpoint": srv.URL + "/authorize", - "token_endpoint": srv.URL + "/token", - "device_authorization_endpoint": srv.URL + "/device", - "code_challenge_methods_supported": []string{"S256"}, - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/device", func(w http.ResponseWriter, r *http.Request) { - _ = r.ParseForm() - w.Header().Set("Content-Type", "application/json") - resp := map[string]any{ - "device_code": "test-device-code", - "user_code": "ABCD-1234", - "verification_uri": "https://example.com/activate", - "verification_uri_complete": "https://example.com/activate?user_code=ABCD-1234", - "expires_in": 300, - "interval": 1, - } - _ = json.NewEncoder(w).Encode(resp) - }) - - mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { - _ = r.ParseForm() - - // Only handle device code grants here. - if r.Form.Get("grant_type") != "urn:ietf:params:oauth:grant-type:device_code" { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"error":"unsupported_grant_type"}`)) - return - } - - count := pollCount.Add(1) - if int(count) <= pendingPolls { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"error":"authorization_pending"}`)) - return - } - - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(tokenResponseJSON("device-access-token", "device-refresh-token", 3600))) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - return srv -} - -// TestDeviceLogin_Success verifies the happy path: the device code flow -// requests a device code, displays it, polls until authorized, and -// returns a valid token. -func TestDeviceLogin_Success(t *testing.T) { - resetDiscoveryCache() - - // Provider returns "authorization_pending" for the first 2 polls, - // then returns a token on the 3rd poll. - provider := setupDeviceMockProvider(t, 2) - - var displayedURL, displayedCode string - - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - tok, err := DeviceLogin(ctx, - WithIssuer(provider.URL), - WithClientID("device-client"), - WithDisplayFunc(func(verificationURL, userCode string) { - displayedURL = verificationURL - displayedCode = userCode - }), - ) - require.NoError(t, err) - assert.Equal(t, "device-access-token", tok.AccessToken) - assert.Equal(t, "device-refresh-token", tok.RefreshToken) - - // Verify the display callback was invoked with correct values. - assert.Equal(t, "https://example.com/activate", displayedURL) - assert.Equal(t, "ABCD-1234", displayedCode) -} - -// TestDeviceLogin_MissingIssuer verifies that DeviceLogin returns -// ErrOIDCConfig when the issuer is not provided. -func TestDeviceLogin_MissingIssuer(t *testing.T) { - resetDiscoveryCache() - - _, err := DeviceLogin(context.Background(), - WithClientID("device-client"), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrOIDCConfig), "expected ErrOIDCConfig, got: %v", err) -} - -// TestDeviceLogin_MissingClientID verifies that DeviceLogin returns -// ErrOIDCConfig when the client ID is not provided. -func TestDeviceLogin_MissingClientID(t *testing.T) { - resetDiscoveryCache() - - _, err := DeviceLogin(context.Background(), - WithIssuer("https://example.com"), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrOIDCConfig), "expected ErrOIDCConfig, got: %v", err) -} - -// TestDeviceLogin_DiscoveryFailure verifies that DeviceLogin returns -// ErrDiscovery when the OIDC provider is unreachable. -func TestDeviceLogin_DiscoveryFailure(t *testing.T) { - resetDiscoveryCache() - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - _, err := DeviceLogin(ctx, - WithIssuer("http://127.0.0.1:1"), - WithClientID("device-client"), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDiscovery), "expected ErrDiscovery, got: %v", err) -} - -// TestDeviceLogin_SlowDown verifies that the polling loop respects the -// "slow_down" response by increasing the polling interval. -func TestDeviceLogin_SlowDown(t *testing.T) { - resetDiscoveryCache() - - mux := http.NewServeMux() - var srv *httptest.Server - var pollCount atomic.Int32 - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "token_endpoint": srv.URL + "/token", - "authorization_endpoint": srv.URL + "/authorize", - "device_authorization_endpoint": srv.URL + "/device", - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/device", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - resp := map[string]any{ - "device_code": "slow-device-code", - "user_code": "SLOW-1234", - "verification_uri": "https://example.com/activate", - "expires_in": 300, - "interval": 1, - } - _ = json.NewEncoder(w).Encode(resp) - }) - - mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { - _ = r.ParseForm() - count := pollCount.Add(1) - w.Header().Set("Content-Type", "application/json") - - switch count { - case 1: - // First poll: slow_down - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"error":"slow_down"}`)) - case 2: - // Second poll: still pending - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"error":"authorization_pending"}`)) - default: - // Third poll: success - _, _ = w.Write([]byte(tokenResponseJSON("slow-token", "", 3600))) - } - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - tok, err := DeviceLogin(ctx, - WithIssuer(srv.URL), - WithClientID("device-client"), - WithDisplayFunc(func(_, _ string) {}), - ) - require.NoError(t, err) - assert.Equal(t, "slow-token", tok.AccessToken) -} - -// TestDeviceLogin_ExpiredDeviceCode verifies that DeviceLogin returns -// ErrDeviceCode when the device code expires before authorization. -func TestDeviceLogin_ExpiredDeviceCode(t *testing.T) { - resetDiscoveryCache() - - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "token_endpoint": srv.URL + "/token", - "authorization_endpoint": srv.URL + "/authorize", - "device_authorization_endpoint": srv.URL + "/device", - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/device", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - resp := map[string]any{ - "device_code": "expiring-device-code", - "user_code": "EXPR-1234", - "verification_uri": "https://example.com/activate", - "expires_in": 300, - "interval": 1, - } - _ = json.NewEncoder(w).Encode(resp) - }) - - mux.HandleFunc("/token", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"error":"expired_token","error_description":"device code expired"}`)) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - _, err := DeviceLogin(ctx, - WithIssuer(srv.URL), - WithClientID("device-client"), - WithDisplayFunc(func(_, _ string) {}), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDeviceCode), "expected ErrDeviceCode, got: %v", err) -} - -// TestDeviceLogin_CustomDisplayFunc verifies that WithDisplayFunc is -// invoked with the verification URL and user code. -func TestDeviceLogin_CustomDisplayFunc(t *testing.T) { - resetDiscoveryCache() - - // Provider that immediately returns a token (0 pending polls). - provider := setupDeviceMockProvider(t, 0) - - var called bool - var capturedURL, capturedCode string - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - tok, err := DeviceLogin(ctx, - WithIssuer(provider.URL), - WithClientID("device-client"), - WithDisplayFunc(func(verificationURL, userCode string) { - called = true - capturedURL = verificationURL - capturedCode = userCode - }), - ) - require.NoError(t, err) - assert.Equal(t, "device-access-token", tok.AccessToken) - assert.True(t, called, "display function should have been called") - assert.Equal(t, "https://example.com/activate", capturedURL) - assert.Equal(t, "ABCD-1234", capturedCode) -} - -// TestDeviceLogin_WithGateway verifies that DeviceLogin resolves OIDC -// config from gateway metadata when WithGateway is set. -func TestDeviceLogin_WithGateway(t *testing.T) { - resetDiscoveryCache() - - provider := setupDeviceMockProvider(t, 0) - - fakeConfig := &gateway.Config{ - Name: "device-gw", - Endpoint: "gateway.example.com:443", - Dir: t.TempDir(), - OIDCIssuer: provider.URL, - OIDCClientID: "gw-device-client", - } - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - tok, err := DeviceLogin(ctx, - WithGateway("device-gw"), - WithDisplayFunc(func(_, _ string) {}), - withGatewayResolver(func(name string) (*gateway.Config, error) { - assert.Equal(t, "device-gw", name) - return fakeConfig, nil - }), - ) - require.NoError(t, err) - assert.Equal(t, "device-access-token", tok.AccessToken) -} - -// TestDeviceLogin_ContextCancellation verifies that DeviceLogin -// respects context cancellation during polling. -func TestDeviceLogin_ContextCancellation(t *testing.T) { - resetDiscoveryCache() - - // Provider that always returns "authorization_pending" so - // the polling loop never succeeds on its own. - provider := setupDeviceMockProvider(t, 1000) - - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - defer cancel() - - _, err := DeviceLogin(ctx, - WithIssuer(provider.URL), - WithClientID("device-client"), - WithDisplayFunc(func(_, _ string) {}), - ) - require.Error(t, err) - // Should be ErrTimeout or context.DeadlineExceeded wrapped. - assert.True(t, - errors.Is(err, ErrTimeout) || errors.Is(err, context.DeadlineExceeded), - "expected timeout error, got: %v", err, - ) -} - -func TestDeviceLogin_WithTimeoutBoundsPolling(t *testing.T) { - resetDiscoveryCache() - provider := setupDeviceMockProvider(t, 1000) - - started := time.Now() - _, err := DeviceLogin(context.Background(), - WithIssuer(provider.URL), - WithClientID("device-client"), - WithTimeout(100*time.Millisecond), - WithDisplayFunc(func(_, _ string) {}), - ) - require.Error(t, err) - assert.ErrorIs(t, err, ErrTimeout) - assert.Less(t, time.Since(started), time.Second) -} - -// TestDeviceLogin_AccessDenied verifies that DeviceLogin returns -// ErrDeviceCode when the user denies authorization. -func TestDeviceLogin_AccessDenied(t *testing.T) { - resetDiscoveryCache() - - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "token_endpoint": srv.URL + "/token", - "authorization_endpoint": srv.URL + "/authorize", - "device_authorization_endpoint": srv.URL + "/device", - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/device", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - resp := map[string]any{ - "device_code": "denied-device-code", - "user_code": "DENY-1234", - "verification_uri": "https://example.com/activate", - "expires_in": 300, - "interval": 1, - } - _ = json.NewEncoder(w).Encode(resp) - }) - - mux.HandleFunc("/token", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"error":"access_denied","error_description":"user denied"}`)) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - _, err := DeviceLogin(ctx, - WithIssuer(srv.URL), - WithClientID("device-client"), - WithDisplayFunc(func(_, _ string) {}), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDeviceCode), "expected ErrDeviceCode, got: %v", err) - assert.Contains(t, err.Error(), "access denied") -} - -// TestDeviceLogin_UnknownError verifies that DeviceLogin returns -// ErrDeviceCode with the error description for unknown error codes. -func TestDeviceLogin_UnknownError(t *testing.T) { - resetDiscoveryCache() - - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "token_endpoint": srv.URL + "/token", - "authorization_endpoint": srv.URL + "/authorize", - "device_authorization_endpoint": srv.URL + "/device", - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/device", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - resp := map[string]any{ - "device_code": "unknown-err-code", - "user_code": "UNKN-1234", - "verification_uri": "https://example.com/activate", - "expires_in": 300, - "interval": 1, - } - _ = json.NewEncoder(w).Encode(resp) - }) - - mux.HandleFunc("/token", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"error":"server_error","error_description":"internal failure"}`)) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - _, err := DeviceLogin(ctx, - WithIssuer(srv.URL), - WithClientID("device-client"), - WithDisplayFunc(func(_, _ string) {}), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDeviceCode), "expected ErrDeviceCode, got: %v", err) - assert.Contains(t, err.Error(), "server_error") - assert.Contains(t, err.Error(), "internal failure") -} - -// TestDeviceLogin_DeviceEndpointHTTPError verifies that DeviceLogin -// returns ErrDeviceCode when the device authorization endpoint returns -// a non-200 HTTP status. -func TestDeviceLogin_DeviceEndpointHTTPError(t *testing.T) { - resetDiscoveryCache() - - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "token_endpoint": srv.URL + "/token", - "authorization_endpoint": srv.URL + "/authorize", - "device_authorization_endpoint": srv.URL + "/device", - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/device", func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - _, _ = w.Write([]byte("internal server error")) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - _, err := DeviceLogin(ctx, - WithIssuer(srv.URL), - WithClientID("device-client"), - WithDisplayFunc(func(_, _ string) {}), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDeviceCode), "expected ErrDeviceCode, got: %v", err) -} - -// TestDeviceLogin_DeviceEndpointInvalidJSON verifies that DeviceLogin -// returns ErrDeviceCode when the device endpoint returns invalid JSON. -func TestDeviceLogin_DeviceEndpointInvalidJSON(t *testing.T) { - resetDiscoveryCache() - - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "token_endpoint": srv.URL + "/token", - "authorization_endpoint": srv.URL + "/authorize", - "device_authorization_endpoint": srv.URL + "/device", - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/device", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(`{not valid json`)) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - _, err := DeviceLogin(ctx, - WithIssuer(srv.URL), - WithClientID("device-client"), - WithDisplayFunc(func(_, _ string) {}), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDeviceCode), "expected ErrDeviceCode, got: %v", err) -} - -// TestDeviceLogin_MissingUserCode verifies that DeviceLogin returns -// ErrDeviceCode when the device endpoint returns an empty user_code. -func TestDeviceLogin_MissingUserCode(t *testing.T) { - resetDiscoveryCache() - - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "token_endpoint": srv.URL + "/token", - "authorization_endpoint": srv.URL + "/authorize", - "device_authorization_endpoint": srv.URL + "/device", - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/device", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - resp := map[string]any{ - "device_code": "code-but-no-user", - "user_code": "", - "verification_uri": "https://example.com/activate", - "expires_in": 300, - "interval": 1, - } - _ = json.NewEncoder(w).Encode(resp) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - _, err := DeviceLogin(ctx, - WithIssuer(srv.URL), - WithClientID("device-client"), - WithDisplayFunc(func(_, _ string) {}), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDeviceCode), "expected ErrDeviceCode, got: %v", err) -} - -// TestDeviceLogin_TokenEndpointInvalidJSON verifies that DeviceLogin -// returns ErrDeviceCode when the token endpoint returns invalid JSON -// during polling. -func TestDeviceLogin_TokenEndpointInvalidJSON(t *testing.T) { - resetDiscoveryCache() - - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "token_endpoint": srv.URL + "/token", - "authorization_endpoint": srv.URL + "/authorize", - "device_authorization_endpoint": srv.URL + "/device", - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/device", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - resp := map[string]any{ - "device_code": "json-err-code", - "user_code": "JSON-1234", - "verification_uri": "https://example.com/activate", - "expires_in": 300, - "interval": 1, - } - _ = json.NewEncoder(w).Encode(resp) - }) - - mux.HandleFunc("/token", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(`{invalid json`)) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - _, err := DeviceLogin(ctx, - WithIssuer(srv.URL), - WithClientID("device-client"), - WithDisplayFunc(func(_, _ string) {}), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDeviceCode), "expected ErrDeviceCode, got: %v", err) -} - -// TestDeviceLogin_NoDeviceEndpoint verifies that DeviceLogin returns -// ErrDeviceCode when the provider does not advertise a device -// authorization endpoint. -func TestDeviceLogin_NoDeviceEndpoint(t *testing.T) { - resetDiscoveryCache() - - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "token_endpoint": srv.URL + "/token", - "authorization_endpoint": srv.URL + "/authorize", - // No device_authorization_endpoint. - } - _ = json.NewEncoder(w).Encode(doc) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - _, err := DeviceLogin(ctx, - WithIssuer(srv.URL), - WithClientID("device-client"), - WithDisplayFunc(func(_, _ string) {}), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDeviceCode), "expected ErrDeviceCode, got: %v", err) -} diff --git a/sdk/go/openshell/v1/oidc/discovery.go b/sdk/go/openshell/v1/oidc/discovery.go deleted file mode 100644 index 1c1dcef2a2..0000000000 --- a/sdk/go/openshell/v1/oidc/discovery.go +++ /dev/null @@ -1,182 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net" - "net/http" - "net/url" - "strings" - "sync" - "time" -) - -var oidcHTTPClient = &http.Client{ - Timeout: 30 * time.Second, - CheckRedirect: func(*http.Request, []*http.Request) error { - return http.ErrUseLastResponse - }, -} - -// providerConfig holds parsed fields from an OIDC discovery document -// (.well-known/openid-configuration). -type providerConfig struct { - Issuer string `json:"issuer"` - AuthorizationEndpoint string `json:"authorization_endpoint"` - TokenEndpoint string `json:"token_endpoint"` - DeviceAuthorizationEndpoint string `json:"device_authorization_endpoint"` - ScopesSupported []string `json:"scopes_supported"` - CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"` -} - -const discoveryCacheTTL = 10 * time.Minute - -type discoveryCacheEntry struct { - config *providerConfig - fetchedAt time.Time -} - -// discoveryCache stores successfully fetched provider configurations -// keyed by normalized issuer URL. Entries expire after discoveryCacheTTL -// so that endpoint rotations are picked up without a process restart. -// Errors are not cached so that transient failures do not permanently -// poison the cache. -var ( - discoveryCacheMu sync.Mutex - discoveryCache = make(map[string]*discoveryCacheEntry) -) - -// resetDiscoveryCache clears the in-memory discovery cache. This is -// only used by tests to avoid interference between test cases. -func resetDiscoveryCache() { - discoveryCacheMu.Lock() - defer discoveryCacheMu.Unlock() - discoveryCache = make(map[string]*discoveryCacheEntry) -} - -// normalizeIssuer strips a trailing slash from the issuer URL so that -// "https://auth.example.com" and "https://auth.example.com/" resolve -// to the same cache key. -func normalizeIssuer(issuer string) string { - return strings.TrimRight(issuer, "/") -} - -// discover fetches and caches the OIDC discovery document for the -// given issuer URL. Only successful results are cached; failed -// fetches are retried on the next call. -func discover(ctx context.Context, issuer string) (*providerConfig, error) { - key := normalizeIssuer(issuer) - now := time.Now() - - discoveryCacheMu.Lock() - if entry, ok := discoveryCache[key]; ok && now.Before(entry.fetchedAt.Add(discoveryCacheTTL)) { - discoveryCacheMu.Unlock() - return entry.config, nil - } - discoveryCacheMu.Unlock() - - cfg, err := fetchDiscovery(ctx, key) - if err != nil { - return nil, err - } - - discoveryCacheMu.Lock() - if entry, ok := discoveryCache[key]; ok && now.Before(entry.fetchedAt.Add(discoveryCacheTTL)) { - discoveryCacheMu.Unlock() - return entry.config, nil - } - discoveryCache[key] = &discoveryCacheEntry{config: cfg, fetchedAt: now} - discoveryCacheMu.Unlock() - - return cfg, nil -} - -// fetchDiscovery performs the actual HTTP GET to the OIDC discovery -// endpoint and parses the response. -func fetchDiscovery(ctx context.Context, issuer string) (*providerConfig, error) { - if err := validateSecureURL("issuer", issuer); err != nil { - return nil, err - } - url := issuer + "/.well-known/openid-configuration" - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("%w: %v", ErrDiscovery, err) - } - - resp, err := oidcHTTPClient.Do(req) - if err != nil { - return nil, fmt.Errorf("%w: %v", ErrDiscovery, err) - } - defer func() { _ = resp.Body.Close() }() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%w: discovery endpoint returned HTTP %d", ErrDiscovery, resp.StatusCode) - } - - const maxResponseBytes = 1 << 20 - body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes+1)) - if err != nil { - return nil, fmt.Errorf("%w: failed to read discovery response: %v", ErrDiscovery, err) - } - if len(body) > maxResponseBytes { - return nil, fmt.Errorf("%w: discovery response is too large", ErrDiscovery) - } - - var cfg providerConfig - if err := json.Unmarshal(body, &cfg); err != nil { - return nil, fmt.Errorf("%w: invalid discovery JSON: %v", ErrDiscovery, err) - } - if normalizeIssuer(cfg.Issuer) != normalizeIssuer(issuer) { - return nil, fmt.Errorf("%w: discovery issuer does not match configured issuer", ErrDiscovery) - } - - if cfg.TokenEndpoint == "" { - return nil, fmt.Errorf("%w: discovery document missing token_endpoint", ErrDiscovery) - } - if cfg.AuthorizationEndpoint == "" { - return nil, fmt.Errorf("%w: discovery document missing authorization_endpoint", ErrDiscovery) - } - for name, endpoint := range map[string]string{ - "issuer": cfg.Issuer, - "authorization_endpoint": cfg.AuthorizationEndpoint, - "token_endpoint": cfg.TokenEndpoint, - "device_authorization_endpoint": cfg.DeviceAuthorizationEndpoint, - } { - if endpoint != "" { - if err := validateSecureURL(name, endpoint); err != nil { - return nil, err - } - } - } - - return &cfg, nil -} - -func validateSecureURL(name, raw string) error { - u, err := url.Parse(raw) - if err != nil || u.Host == "" { - return fmt.Errorf("%w: invalid %s URL", ErrDiscovery, name) - } - if u.User != nil || u.Fragment != "" { - return fmt.Errorf("%w: %s URL must not contain userinfo or a fragment", ErrDiscovery, name) - } - if u.Scheme == "https" { - return nil - } - host := u.Hostname() - if u.Scheme == "http" && (strings.EqualFold(host, "localhost") || isLoopbackIP(host)) { - return nil - } - return fmt.Errorf("%w: %s URL must use HTTPS (HTTP is allowed only for loopback hosts)", ErrDiscovery, name) -} - -func isLoopbackIP(host string) bool { - ip := net.ParseIP(host) - return ip != nil && ip.IsLoopback() -} diff --git a/sdk/go/openshell/v1/oidc/discovery_test.go b/sdk/go/openshell/v1/oidc/discovery_test.go deleted file mode 100644 index ede7218785..0000000000 --- a/sdk/go/openshell/v1/oidc/discovery_test.go +++ /dev/null @@ -1,263 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "context" - "errors" - "net/http" - "net/http/httptest" - "sync" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// wellKnownJSON returns a valid OIDC discovery document JSON string -// with the given issuer URL as the base. -func wellKnownJSON(issuer string) string { - return `{ - "issuer": "` + issuer + `", - "authorization_endpoint": "` + issuer + `/authorize", - "token_endpoint": "` + issuer + `/token", - "device_authorization_endpoint": "` + issuer + `/device", - "scopes_supported": ["openid", "profile", "email"], - "code_challenge_methods_supported": ["S256"] - }` -} - -func TestDiscover_ValidDocument(t *testing.T) { - var srv *httptest.Server - srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(wellKnownJSON(srv.URL))) - })) - defer srv.Close() - - // Clear cache to avoid interference from other tests. - resetDiscoveryCache() - - cfg, err := discover(context.Background(), srv.URL) - require.NoError(t, err) - - assert.Equal(t, srv.URL, cfg.Issuer) - assert.Equal(t, srv.URL+"/authorize", cfg.AuthorizationEndpoint) - assert.Equal(t, srv.URL+"/token", cfg.TokenEndpoint) - assert.Equal(t, srv.URL+"/device", cfg.DeviceAuthorizationEndpoint) - assert.Equal(t, []string{"openid", "profile", "email"}, cfg.ScopesSupported) - assert.Equal(t, []string{"S256"}, cfg.CodeChallengeMethodsSupported) -} - -func TestDiscover_CachesResult(t *testing.T) { - callCount := 0 - var srv *httptest.Server - srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - callCount++ - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(wellKnownJSON(srv.URL))) - })) - defer srv.Close() - - resetDiscoveryCache() - - cfg1, err := discover(context.Background(), srv.URL) - require.NoError(t, err) - - cfg2, err := discover(context.Background(), srv.URL) - require.NoError(t, err) - - // Same pointer should be returned from cache. - assert.Same(t, cfg1, cfg2) - assert.Equal(t, 1, callCount, "discovery should be fetched only once") -} - -func TestDiscover_DifferentIssuersNotCached(t *testing.T) { - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - issuer := "http://" + r.Host - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(wellKnownJSON(issuer))) - }) - - srv1 := httptest.NewServer(handler) - defer srv1.Close() - srv2 := httptest.NewServer(handler) - defer srv2.Close() - - resetDiscoveryCache() - - cfg1, err := discover(context.Background(), srv1.URL) - require.NoError(t, err) - - cfg2, err := discover(context.Background(), srv2.URL) - require.NoError(t, err) - - // Different issuers should yield different cached entries. - assert.NotSame(t, cfg1, cfg2) -} - -func TestDiscover_HTTPError(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - })) - defer srv.Close() - - resetDiscoveryCache() - - _, err := discover(context.Background(), srv.URL) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDiscovery)) -} - -func TestDiscover_InvalidJSON(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(`{not valid json}`)) - })) - defer srv.Close() - - resetDiscoveryCache() - - _, err := discover(context.Background(), srv.URL) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDiscovery)) -} - -func TestDiscover_MissingTokenEndpoint(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - issuer := "http://" + r.Host - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(`{"issuer":"` + issuer + `","authorization_endpoint":"` + issuer + `/authorize"}`)) - })) - defer srv.Close() - - resetDiscoveryCache() - - _, err := discover(context.Background(), srv.URL) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDiscovery)) - assert.Contains(t, err.Error(), "token_endpoint") -} - -func TestDiscover_ContextCancelled(t *testing.T) { - var srv *httptest.Server - srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(wellKnownJSON(srv.URL))) - })) - defer srv.Close() - - resetDiscoveryCache() - - ctx, cancel := context.WithCancel(context.Background()) - cancel() // Cancel immediately. - - _, err := discover(ctx, srv.URL) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDiscovery)) -} - -func TestDiscover_ConcurrentAccess(t *testing.T) { - callCount := 0 - var mu sync.Mutex - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - mu.Lock() - callCount++ - mu.Unlock() - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(wellKnownJSON("http://" + r.Host))) - })) - defer srv.Close() - - resetDiscoveryCache() - - var wg sync.WaitGroup - results := make([]*providerConfig, 10) - errs := make([]error, 10) - - for i := range 10 { - wg.Add(1) - go func(idx int) { - defer wg.Done() - results[idx], errs[idx] = discover(context.Background(), srv.URL) - }(i) - } - wg.Wait() - - for i := range 10 { - require.NoError(t, errs[i]) - assert.NotNil(t, results[i]) - } - - // Without sync.Once, concurrent goroutines may each fetch before - // the first result is cached. All calls should succeed, and the - // server should be called at most once per concurrent racer (not - // 10 times if caching works at all). In practice, most calls - // should hit the cache after the first fetch completes. - mu.Lock() - defer mu.Unlock() - assert.LessOrEqual(t, callCount, 10, "caching should reduce total fetches") -} - -func TestDiscover_NoDeviceEndpointIsOK(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - issuer := "http://" + r.Host - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(`{"issuer":"` + issuer + `","authorization_endpoint":"` + issuer + `/authorize","token_endpoint":"` + issuer + `/token"}`)) - })) - defer srv.Close() - - resetDiscoveryCache() - - cfg, err := discover(context.Background(), srv.URL) - require.NoError(t, err) - assert.Empty(t, cfg.DeviceAuthorizationEndpoint) -} - -func TestDiscover_TrailingSlashNormalized(t *testing.T) { - callCount := 0 - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - callCount++ - issuer := "http://" + r.Host - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(wellKnownJSON(issuer))) - })) - defer srv.Close() - - resetDiscoveryCache() - - // Call with trailing slash and without; should be same cache entry. - _, err := discover(context.Background(), srv.URL) - require.NoError(t, err) - - _, err = discover(context.Background(), srv.URL+"/") - require.NoError(t, err) - - assert.Equal(t, 1, callCount, "trailing slash should be normalized for caching") -} - -func TestDiscover_RejectsIssuerMismatch(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - _, _ = w.Write([]byte(wellKnownJSON("https://attacker.example"))) - })) - defer srv.Close() - resetDiscoveryCache() - - _, err := discover(context.Background(), srv.URL) - require.Error(t, err) - assert.Contains(t, err.Error(), "issuer") -} - -func TestDiscover_RejectsInsecureRemoteEndpoint(t *testing.T) { - var srv *httptest.Server - srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - _, _ = w.Write([]byte(`{"issuer":"` + srv.URL + `","authorization_endpoint":"http://example.com/authorize","token_endpoint":"` + srv.URL + `/token"}`)) - })) - defer srv.Close() - resetDiscoveryCache() - - _, err := discover(context.Background(), srv.URL) - require.Error(t, err) - assert.Contains(t, err.Error(), "authorization_endpoint") -} diff --git a/sdk/go/openshell/v1/oidc/doc.go b/sdk/go/openshell/v1/oidc/doc.go deleted file mode 100644 index a89d4f2206..0000000000 --- a/sdk/go/openshell/v1/oidc/doc.go +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package oidc provides OIDC authentication flows for the OpenShell SDK. -// -// The package supports four authentication flows: -// -// - Authorization Code with PKCE (interactive browser-based login) -// - Keyboard flow (manual URL copy and code paste for headless environments) -// - Device Code flow (RFC 8628, for input-constrained devices) -// - Client Credentials grant (non-interactive service account authentication) -// -// # Gateway-Aware Login -// -// The primary use case is gateway-aware login, where OIDC provider -// configuration is read from a gateway's metadata.json file: -// -// token, err := oidc.Login(ctx, "my-gateway") -// if err != nil { -// log.Fatal(err) -// } -// -// After successful authentication, tokens are persisted to disk in the -// gateway directory as oidc_token.json, in the same format as -// `openshell gateway login`, so both [gateway.NewClient] and the CLI can -// use them. A later Login reuses the stored token only when it is valid -// and was issued for the gateway's issuer and client ID. -// -// # Standalone Login -// -// For OIDC providers not tied to an OpenShell gateway, use explicit -// configuration: -// -// token, err := oidc.Login(ctx, "", -// oidc.WithIssuer("https://auth.example.com"), -// oidc.WithClientID("my-app"), -// oidc.WithInMemory(), -// ) -// -// # Device Code Flow -// -// For environments without a browser: -// -// token, err := oidc.DeviceLogin(ctx, -// oidc.WithIssuer("https://auth.example.com"), -// oidc.WithClientID("my-app"), -// ) -// -// # Client Credentials -// -// For non-interactive service accounts: -// -// auth, err := oidc.NewClientCredentialsAuth( -// oidc.WithIssuer("https://auth.example.com"), -// oidc.WithClientID("my-service"), -// oidc.WithClientSecret("secret"), -// ) -// -// # Error Handling -// -// The package provides typed sentinel errors for precise failure -// classification: -// -// - [ErrDiscovery]: OIDC discovery fetch or parse failed -// - [ErrAuthCode]: Authorization code exchange failed -// - [ErrDeviceCode]: Device code flow failed -// - [ErrClientCredentials]: Client credentials exchange failed -// - [ErrTimeout]: Interactive flow timed out -// - [ErrCallbackServer]: Localhost callback server failed to start -// - [ErrTokenPersist]: Token disk write failed -// - [ErrOIDCConfig]: Gateway metadata missing OIDC fields -// -// All errors support [errors.Is] for classification. -// -// # Thread Safety -// -// All exported functions are safe for concurrent use from multiple -// goroutines. OIDC discovery documents are cached in memory per issuer -// URL for the lifetime of the process. -package oidc diff --git a/sdk/go/openshell/v1/oidc/errors.go b/sdk/go/openshell/v1/oidc/errors.go deleted file mode 100644 index 20c9105c03..0000000000 --- a/sdk/go/openshell/v1/oidc/errors.go +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import "errors" - -// Sentinel errors for OIDC authentication failures. All wrapped errors -// returned by this package support classification via [errors.Is]. -var ( - // ErrDiscovery is returned when the OIDC discovery document - // (.well-known/openid-configuration) cannot be fetched or parsed. - ErrDiscovery = errors.New("oidc: discovery failed") - - // ErrAuthCode is returned when the authorization code exchange - // fails (invalid code, expired code, provider error). - ErrAuthCode = errors.New("oidc: auth code exchange failed") - - // ErrDeviceCode is returned when the device code flow fails - // (request error, expired device code, provider error). - ErrDeviceCode = errors.New("oidc: device code flow failed") - - // ErrClientCredentials is returned when the client credentials - // grant fails (invalid credentials, provider error). The error - // message never contains the client secret. - ErrClientCredentials = errors.New("oidc: client credentials exchange failed") - - // ErrTimeout is returned when an interactive login flow - // (browser, keyboard, or device code) exceeds its deadline. - ErrTimeout = errors.New("oidc: login timed out") - - // ErrCallbackServer is returned when the localhost HTTP server - // for the authorization code redirect cannot bind to any port. - ErrCallbackServer = errors.New("oidc: callback server failed") - - // ErrTokenPersist is returned when the token cannot be written - // to disk (permission error, invalid path). - ErrTokenPersist = errors.New("oidc: token persistence failed") - - // ErrOIDCConfig is returned when gateway metadata is missing - // the required oidc_issuer or oidc_client_id fields. - ErrOIDCConfig = errors.New("oidc: gateway OIDC config missing") -) diff --git a/sdk/go/openshell/v1/oidc/errors_test.go b/sdk/go/openshell/v1/oidc/errors_test.go deleted file mode 100644 index ea9dfb5a10..0000000000 --- a/sdk/go/openshell/v1/oidc/errors_test.go +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "errors" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestSentinelErrors_AreDistinct(t *testing.T) { - sentinels := []error{ - ErrDiscovery, - ErrAuthCode, - ErrDeviceCode, - ErrClientCredentials, - ErrTimeout, - ErrCallbackServer, - ErrTokenPersist, - ErrOIDCConfig, - } - - for i, a := range sentinels { - for j, b := range sentinels { - if i == j { - continue - } - assert.False(t, errors.Is(a, b), - "expected %v and %v to be distinct", a, b) - } - } -} - -func TestSentinelErrors_MatchSelf(t *testing.T) { - sentinels := []error{ - ErrDiscovery, - ErrAuthCode, - ErrDeviceCode, - ErrClientCredentials, - ErrTimeout, - ErrCallbackServer, - ErrTokenPersist, - ErrOIDCConfig, - } - - for _, sentinel := range sentinels { - assert.True(t, errors.Is(sentinel, sentinel), - "expected %v to match itself", sentinel) - } -} - -func TestSentinelErrors_WrappedMatchViIs(t *testing.T) { - cases := []struct { - name string - sentinel error - }{ - {"ErrDiscovery", ErrDiscovery}, - {"ErrAuthCode", ErrAuthCode}, - {"ErrDeviceCode", ErrDeviceCode}, - {"ErrClientCredentials", ErrClientCredentials}, - {"ErrTimeout", ErrTimeout}, - {"ErrCallbackServer", ErrCallbackServer}, - {"ErrTokenPersist", ErrTokenPersist}, - {"ErrOIDCConfig", ErrOIDCConfig}, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - wrapped := fmt.Errorf("operation failed: %w", tc.sentinel) - assert.True(t, errors.Is(wrapped, tc.sentinel), - "wrapped error should match sentinel via errors.Is") - }) - } -} - -func TestSentinelErrors_HaveDescriptiveMessages(t *testing.T) { - cases := []struct { - sentinel error - contains string - }{ - {ErrDiscovery, "discovery"}, - {ErrAuthCode, "auth code"}, - {ErrDeviceCode, "device code"}, - {ErrClientCredentials, "client credentials"}, - {ErrTimeout, "timed out"}, - {ErrCallbackServer, "callback server"}, - {ErrTokenPersist, "token persistence"}, - {ErrOIDCConfig, "OIDC config"}, - } - - for _, tc := range cases { - t.Run(tc.sentinel.Error(), func(t *testing.T) { - assert.Contains(t, tc.sentinel.Error(), tc.contains) - }) - } -} - -func TestSentinelErrors_DoubleWrapped(t *testing.T) { - inner := fmt.Errorf("http timeout: %w", ErrDiscovery) - outer := fmt.Errorf("login failed: %w", inner) - - assert.True(t, errors.Is(outer, ErrDiscovery), - "double-wrapped error should still match sentinel") -} diff --git a/sdk/go/openshell/v1/oidc/example_test.go b/sdk/go/openshell/v1/oidc/example_test.go deleted file mode 100644 index c304ef0bfa..0000000000 --- a/sdk/go/openshell/v1/oidc/example_test.go +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// These examples demonstrate OIDC package usage but are guarded from -// execution during `go test` because they require real network access -// and user interaction. The guard `if false` keeps the code type-checked -// by the compiler without executing during tests. - -package oidc_test - -import ( - "context" - "fmt" - "log" - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/oidc" -) - -func ExampleLogin_gateway() { - if false { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - - token, err := oidc.Login(ctx, "my-gateway") - if err != nil { - log.Fatal(err) - } - - fmt.Printf("Authenticated. Token expires at %s\n", token.Expiry.Format(time.RFC3339)) - } - - fmt.Println("ok") - // Output: ok -} - -func ExampleLogin_standalone() { - if false { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - - token, err := oidc.Login(ctx, "", - oidc.WithIssuer("https://auth.example.com"), - oidc.WithClientID("my-app"), - oidc.WithInMemory(), - ) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("Access token: %s...\n", token.AccessToken[:10]) - } - - fmt.Println("ok") - // Output: ok -} - -func ExampleLogin_keyboard() { - if false { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - token, err := oidc.Login(ctx, "my-gateway", - oidc.WithKeyboardFlow(), - ) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("Authenticated via keyboard flow. Token type: %s\n", token.TokenType) - } - - fmt.Println("ok") - // Output: ok -} - -func ExampleDeviceLogin() { - if false { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - token, err := oidc.DeviceLogin(ctx, - oidc.WithIssuer("https://auth.example.com"), - oidc.WithClientID("my-device-app"), - ) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("Device authorized. Token expires at %s\n", token.Expiry.Format(time.RFC3339)) - } - - fmt.Println("ok") - // Output: ok -} - -func ExampleDeviceLogin_customDisplay() { - if false { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - token, err := oidc.DeviceLogin(ctx, - oidc.WithIssuer("https://auth.example.com"), - oidc.WithClientID("my-tui-app"), - oidc.WithDisplayFunc(func(verificationURL, userCode string) { - fmt.Printf("Please visit: %s\n", verificationURL) - fmt.Printf("Enter code: %s\n", userCode) - }), - ) - if err != nil { - log.Fatal(err) - } - - _ = token - } - - fmt.Println("ok") - // Output: ok -} - -func ExampleClientCredentials() { - if false { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - token, err := oidc.ClientCredentials(ctx, - oidc.WithIssuer("https://auth.example.com"), - oidc.WithClientID("my-service"), - oidc.WithClientSecret("service-secret"), - ) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("Service authenticated. Token type: %s\n", token.TokenType) - } - - fmt.Println("ok") - // Output: ok -} - -func ExampleClientCredentials_gateway() { - if false { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - token, err := oidc.ClientCredentials(ctx, - oidc.WithGateway("my-gateway"), - oidc.WithClientSecret("service-secret"), - ) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("Service authenticated via gateway. Token type: %s\n", token.TokenType) - } - - fmt.Println("ok") - // Output: ok -} diff --git a/sdk/go/openshell/v1/oidc/keyboard.go b/sdk/go/openshell/v1/oidc/keyboard.go deleted file mode 100644 index 3000a7560d..0000000000 --- a/sdk/go/openshell/v1/oidc/keyboard.go +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "bufio" - "context" - "fmt" - "io" - "strings" -) - -type readResult struct { - code string - err error -} - -// keyboardFlow implements the keyboard fallback for the authorization -// code flow. It displays the authorization URL to the user and reads -// the pasted authorization code from the provided reader. -// -// Parameters: -// - ctx: context for cancellation/timeout -// - authURL: the full authorization URL to display -// - input: reader for user input (typically os.Stdin) -// - output: writer for prompts/instructions (typically os.Stderr) -// -// Returns the authorization code or an error. -func keyboardFlow(ctx context.Context, authURL string, input io.Reader, output io.Writer) (string, error) { - // Display instructions and URL. - _, _ = fmt.Fprintf(output, "\nOpen the following URL in your browser to authenticate:\n\n %s\n\n", authURL) - _, _ = fmt.Fprint(output, "Paste the authorization code here and press Enter: ") - - // Read code with context cancellation support. - result, err := keyboardInput.read(ctx, input) - if err != nil { - return "", err - } - if result.err != nil { - return "", result.err - } - if result.code == "" { - return "", fmt.Errorf("%w: empty authorization code", ErrAuthCode) - } - return result.code, nil -} - -type inputRequest struct { - input io.Reader - result chan readResult -} - -type inputDispatcher struct { - requests chan inputRequest -} - -var keyboardInput = newInputDispatcher() - -func newInputDispatcher() *inputDispatcher { - d := &inputDispatcher{requests: make(chan inputRequest)} - go d.run() - return d -} - -func (d *inputDispatcher) read(ctx context.Context, input io.Reader) (readResult, error) { - if err := ctx.Err(); err != nil { - return readResult{}, fmt.Errorf("%w: %v", ErrTimeout, err) - } - request := inputRequest{input: input, result: make(chan readResult, 1)} - select { - case d.requests <- request: - case <-ctx.Done(): - return readResult{}, fmt.Errorf("%w: %v", ErrTimeout, ctx.Err()) - } - select { - case result := <-request.result: - return result, nil - case <-ctx.Done(): - return readResult{}, fmt.Errorf("%w: %v", ErrTimeout, ctx.Err()) - } -} - -func (d *inputDispatcher) run() { - for request := range d.requests { - scanner := bufio.NewScanner(request.input) - if scanner.Scan() { - request.result <- readResult{code: strings.TrimSpace(scanner.Text())} - continue - } - if err := scanner.Err(); err != nil { - request.result <- readResult{err: fmt.Errorf("%w: failed to read authorization code: %v", ErrAuthCode, err)} - } else { - request.result <- readResult{err: fmt.Errorf("%w: no authorization code received (EOF)", ErrAuthCode)} - } - } -} diff --git a/sdk/go/openshell/v1/oidc/keyboard_test.go b/sdk/go/openshell/v1/oidc/keyboard_test.go deleted file mode 100644 index 080c4888b9..0000000000 --- a/sdk/go/openshell/v1/oidc/keyboard_test.go +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "bytes" - "context" - "errors" - "io" - "strings" - "sync/atomic" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// T014: Keyboard fallback flow tests - -func TestKeyboardFlow_ReadsCode(t *testing.T) { - // Simulate user pasting a code via stdin. - input := strings.NewReader("my-auth-code\n") - output := &bytes.Buffer{} - - code, err := keyboardFlow( - context.Background(), - "https://auth.example.com/authorize?client_id=test", - input, - output, - ) - require.NoError(t, err) - assert.Equal(t, "my-auth-code", code) - - // Verify that the URL was displayed to the user. - assert.Contains(t, output.String(), "https://auth.example.com/authorize?client_id=test") -} - -func TestInputDispatcher_BoundsBlockedReads(t *testing.T) { - d := newInputDispatcher() - reader := &countingBlockingReader{release: make(chan struct{})} - t.Cleanup(func() { close(reader.release) }) - - for range 5 { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) - _, err := d.read(ctx, reader) - cancel() - require.Error(t, err) - } - assert.Equal(t, int32(1), reader.max.Load()) -} - -func TestKeyboardFlow_TrimsWhitespace(t *testing.T) { - input := strings.NewReader(" some-code-with-spaces \n") - output := &bytes.Buffer{} - - code, err := keyboardFlow( - context.Background(), - "https://auth.example.com/authorize", - input, - output, - ) - require.NoError(t, err) - assert.Equal(t, "some-code-with-spaces", code) -} - -func TestKeyboardFlow_EmptyInput(t *testing.T) { - input := strings.NewReader("\n") - output := &bytes.Buffer{} - - _, err := keyboardFlow( - context.Background(), - "https://auth.example.com/authorize", - input, - output, - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrAuthCode)) -} - -func TestKeyboardFlow_EOFBeforeInput(t *testing.T) { - // Reader that returns EOF immediately (e.g., piped /dev/null). - input := strings.NewReader("") - output := &bytes.Buffer{} - - _, err := keyboardFlow( - context.Background(), - "https://auth.example.com/authorize", - input, - output, - ) - require.Error(t, err) - // Should be ErrAuthCode since no code was received. - assert.True(t, errors.Is(err, ErrAuthCode)) -} - -func TestKeyboardFlow_ContextCancelled(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - cancel() // Cancel immediately. - - // Use a reader that blocks forever (until context cancel). - input := &blockingReader{} - output := &bytes.Buffer{} - - _, err := keyboardFlow(ctx, "https://auth.example.com/authorize", input, output) - require.Error(t, err) -} - -func TestKeyboardFlow_DisplaysInstructions(t *testing.T) { - input := strings.NewReader("test-code\n") - output := &bytes.Buffer{} - - _, err := keyboardFlow( - context.Background(), - "https://auth.example.com/authorize?response_type=code", - input, - output, - ) - require.NoError(t, err) - - displayed := output.String() - // Must show the URL and some instruction text. - assert.Contains(t, displayed, "https://auth.example.com/authorize?response_type=code") - // Should prompt user to paste the code. - lower := strings.ToLower(displayed) - assert.True(t, - strings.Contains(lower, "paste") || strings.Contains(lower, "code") || strings.Contains(lower, "enter"), - "output should instruct the user to paste or enter the code", - ) -} - -func TestKeyboardFlow_Timeout(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) - defer cancel() - - // Reader that never returns data. - input := &blockingReader{} - output := &bytes.Buffer{} - - _, err := keyboardFlow(ctx, "https://example.com/auth", input, output) - require.Error(t, err) -} - -// blockingReader is an io.Reader that blocks until the context is cancelled. -// It is used to simulate a user who never types anything. -type blockingReader struct{} - -func (r *blockingReader) Read(_ []byte) (int, error) { - // Block for a long time to simulate waiting for input. - time.Sleep(100 * time.Millisecond) - return 0, io.EOF -} - -type countingBlockingReader struct { - active atomic.Int32 - max atomic.Int32 - release chan struct{} -} - -func (r *countingBlockingReader) Read(_ []byte) (int, error) { - active := r.active.Add(1) - for { - old := r.max.Load() - if active <= old || r.max.CompareAndSwap(old, active) { - break - } - } - <-r.release - r.active.Add(-1) - return 0, io.EOF -} diff --git a/sdk/go/openshell/v1/oidc/oidc.go b/sdk/go/openshell/v1/oidc/oidc.go deleted file mode 100644 index d16aafc4d6..0000000000 --- a/sdk/go/openshell/v1/oidc/oidc.go +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "context" - "fmt" - "io" - "os" - "slices" - - "golang.org/x/oauth2" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/gateway" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/oidctoken" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" -) - -// Login performs an interactive OIDC authorization code login. -// -// When gatewayName is non-empty, Login resolves OIDC configuration -// (issuer URL and client ID) from the gateway's metadata.json file and -// persists tokens to the gateway directory as oidc_token.json, in the -// same format as `openshell gateway login`. -// -// When gatewayName is empty, the caller must provide [WithIssuer] and -// [WithClientID] options explicitly. -// -// Before starting an interactive flow, Login checks for an existing -// token on disk (FR-019). If the stored token is valid and was issued -// for the same issuer and client ID, it is returned immediately without -// user interaction. -// -// The flow attempts to open a browser for authorization. If the browser -// cannot be opened, or if [WithKeyboardFlow] is set, the keyboard -// fallback flow is used instead. -func Login(ctx context.Context, gatewayName string, opts ...LoginOption) (*oauth2.Token, error) { - cfg := &loginConfig{} - options.Apply(cfg, opts) - cfg.applyDefaults() - - // Apply configured timeout if the caller's context has no deadline. - if _, hasDeadline := ctx.Deadline(); !hasDeadline && cfg.timeout > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, cfg.timeout) - defer cancel() - } - - // Resolve OIDC configuration from gateway or explicit options. - tokenDir, err := resolveOIDCConfig(cfg, gatewayName) - if err != nil { - return nil, err - } - - // FR-019: Check for existing valid token on disk before starting - // an interactive flow. - if tokenDir != "" { - stored, readErr := readToken(tokenDir) - if readErr == nil && stored != nil && - normalizeIssuer(stored.Issuer) == normalizeIssuer(cfg.issuer) && - stored.ClientID == cfg.clientID { - if tok := stored.Token(); tok.Valid() { - return tok, nil - } - } - // Unreadable, mismatched, or expired tokens and missing files - // are not errors; we simply proceed to the interactive flow. - } - - // Run OIDC discovery to get provider endpoints. - provider, err := discover(ctx, cfg.issuer) - if err != nil { - return nil, err - } - - // Generate PKCE verifier and challenge if the provider supports S256. - var codeVerifier, codeChallenge string - if supportsS256(provider) { - codeVerifier, err = generateCodeVerifier() - if err != nil { - return nil, fmt.Errorf("%w: %v", ErrAuthCode, err) - } - codeChallenge = codeChallengeS256(codeVerifier) - } - - // Generate cryptographic state for CSRF protection. - state, err := generateState() - if err != nil { - return nil, fmt.Errorf("%w: %v", ErrAuthCode, err) - } - - // Determine the authorization code acquisition method. - // The redirectURI must match exactly between the auth request and - // the token exchange (OIDC/OAuth2 requirement). - var code, redirectURI string - if cfg.keyboardFlow { - redirectURI = "urn:ietf:wg:oauth:2.0:oob" - code, err = loginKeyboard(ctx, cfg, provider, state, codeChallenge) - } else { - code, redirectURI, err = loginBrowser(ctx, cfg, provider, state, codeChallenge) - } - if err != nil { - return nil, err - } - - // Exchange the authorization code for tokens. - tok, err := exchangeCode(ctx, provider.TokenEndpoint, cfg.clientID, code, redirectURI, codeVerifier) - if err != nil { - return nil, err - } - - // Persist token to disk unless in-memory mode is requested. - if !cfg.inMemory && tokenDir != "" { - if writeErr := writeToken(tokenDir, oidctoken.FromToken(tok, cfg.issuer, cfg.clientID)); writeErr != nil { - return nil, writeErr - } - } - - return tok, nil -} - -// resolveOIDCConfig resolves the OIDC issuer and client ID either from -// the gateway metadata or from explicit options. Returns the token -// directory path (empty if in-memory or no directory available). -func resolveOIDCConfig(cfg *loginConfig, gatewayName string) (string, error) { - tokenDir := cfg.tokenDir - - if gatewayName != "" { - // Resolve from gateway. - resolver := cfg.gatewayResolver - if resolver == nil { - resolver = gateway.LoadConfig - } - gwCfg, err := resolver(gatewayName) - if err != nil { - return "", fmt.Errorf("failed to load gateway %q: %w", gatewayName, err) - } - if gwCfg.OIDCIssuer == "" || gwCfg.OIDCClientID == "" { - return "", fmt.Errorf("%w: gateway %q has no OIDC configuration (missing oidc_issuer or oidc_client_id in metadata.json)", ErrOIDCConfig, gatewayName) - } - cfg.issuer = gwCfg.OIDCIssuer - cfg.clientID = gwCfg.OIDCClientID - if tokenDir == "" { - tokenDir = gwCfg.Dir - } - } - - // Validate that we have the minimum required config. - if cfg.issuer == "" || cfg.clientID == "" { - return "", fmt.Errorf("%w: issuer and client ID are required (provide a gateway name or use WithIssuer and WithClientID)", ErrOIDCConfig) - } - - return tokenDir, nil -} - -// supportsS256 checks if the OIDC provider advertises S256 PKCE support. -func supportsS256(provider *providerConfig) bool { - return slices.Contains(provider.CodeChallengeMethodsSupported, "S256") -} - -// loginKeyboard performs the keyboard flow: builds the auth URL, shows -// it to the user, and reads the pasted authorization code. -func loginKeyboard(ctx context.Context, cfg *loginConfig, provider *providerConfig, state, challenge string) (string, error) { - redirectURI := "urn:ietf:wg:oauth:2.0:oob" - authURL := buildAuthURL(provider.AuthorizationEndpoint, cfg.clientID, redirectURI, state, challenge, cfg.scopes) - - input := cfg.input - if input == nil { - input = os.Stdin - } - var output io.Writer = os.Stderr - if cfg.output != nil { - output = cfg.output - } - - return keyboardFlow(ctx, authURL, input, output) -} - -// loginBrowser performs the browser-based flow: starts a callback server, -// opens the browser, and waits for the callback. Falls back to keyboard -// if the browser cannot be opened. -// -// Returns (code, redirectURI, error). The redirectURI must be passed to -// exchangeCode so that it exactly matches the URI used in the auth -// request. When the function falls back to keyboard flow, the returned -// redirectURI is the keyboard placeholder ("urn:ietf:wg:oauth:2.0:oob"). -func loginBrowser(ctx context.Context, cfg *loginConfig, provider *providerConfig, state, challenge string) (string, string, error) { - port := cfg.callbackPort - if port == 0 { - port = 8000 - } - - srv, resultCh, err := startCallbackServer(ctx, port, state) - if err != nil { - // Try fallback port if the primary port failed and no custom - // port was specified. - if cfg.callbackPort == 0 { - port = 18000 - srv, resultCh, err = startCallbackServer(ctx, port, state) - } - if err != nil { - // Cannot start callback server, fall back to keyboard. - code, kbErr := loginKeyboard(ctx, cfg, provider, state, challenge) - return code, "urn:ietf:wg:oauth:2.0:oob", kbErr - } - } - defer func() { - _ = srv.Close() - }() - - redirectURI := fmt.Sprintf("http://localhost:%d/callback", port) - authURL := buildAuthURL(provider.AuthorizationEndpoint, cfg.clientID, redirectURI, state, challenge, cfg.scopes) - - // Try to open the browser. - if browserErr := openBrowser(authURL); browserErr != nil { - // Browser failed, fall back to keyboard flow. - _ = srv.Close() - code, kbErr := loginKeyboard(ctx, cfg, provider, state, challenge) - return code, "urn:ietf:wg:oauth:2.0:oob", kbErr - } - - // Wait for the callback result or context cancellation. - select { - case <-ctx.Done(): - return "", "", fmt.Errorf("%w: %v", ErrTimeout, ctx.Err()) - case result := <-resultCh: - if result.err != nil { - return "", "", result.err - } - return result.code, redirectURI, nil - } -} diff --git a/sdk/go/openshell/v1/oidc/oidc_test.go b/sdk/go/openshell/v1/oidc/oidc_test.go deleted file mode 100644 index 5627cc1b4c..0000000000 --- a/sdk/go/openshell/v1/oidc/oidc_test.go +++ /dev/null @@ -1,547 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "net" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "golang.org/x/oauth2" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/gateway" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/oidctoken" -) - -// --- T021: Login entry point tests --- - -// setupMockProvider creates a mock OIDC provider that serves discovery, -// authorize, and token endpoints. The token endpoint returns a valid -// token response. Returns the server (auto-cleaned up) and its URL. -func setupMockProvider(t *testing.T) *httptest.Server { - t.Helper() - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "authorization_endpoint": srv.URL + "/authorize", - "token_endpoint": srv.URL + "/token", - "device_authorization_endpoint": srv.URL + "/device", - "scopes_supported": []string{"openid", "profile", "email"}, - "code_challenge_methods_supported": []string{"S256"}, - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { - _ = r.ParseForm() - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(tokenResponseJSON("login-access-token", "login-refresh-token", 3600))) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - return srv -} - -// TestLogin_MissingOIDCConfig verifies that Login returns ErrOIDCConfig -// when called without a gateway name and without WithIssuer/WithClientID. -func TestLogin_MissingOIDCConfig(t *testing.T) { - resetDiscoveryCache() - - _, err := Login(context.Background(), "") - require.Error(t, err) - assert.True(t, errors.Is(err, ErrOIDCConfig), "expected ErrOIDCConfig, got: %v", err) -} - -// TestLogin_MissingIssuer verifies that Login returns ErrOIDCConfig -// when only WithClientID is provided (missing issuer). -func TestLogin_MissingIssuer(t *testing.T) { - resetDiscoveryCache() - - _, err := Login(context.Background(), "", WithClientID("test-client")) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrOIDCConfig), "expected ErrOIDCConfig, got: %v", err) -} - -// TestLogin_MissingClientID verifies that Login returns ErrOIDCConfig -// when only WithIssuer is provided (missing client ID). -func TestLogin_MissingClientID(t *testing.T) { - resetDiscoveryCache() - - _, err := Login(context.Background(), "", WithIssuer("https://example.com")) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrOIDCConfig), "expected ErrOIDCConfig, got: %v", err) -} - -// TestLogin_ReusesValidToken verifies FR-019: when a valid token for the -// same issuer (ignoring a trailing slash) and client ID exists on disk, -// Login returns it without starting an interactive flow. -func TestLogin_ReusesValidToken(t *testing.T) { - resetDiscoveryCache() - - // Create a temp dir with a valid, non-expired token file. - tokenDir := t.TempDir() - existingToken := &oauth2.Token{ - AccessToken: "existing-access-token", - RefreshToken: "existing-refresh-token", - TokenType: "Bearer", - Expiry: time.Now().Add(1 * time.Hour), - } - err := writeToken(tokenDir, oidctoken.FromToken(existingToken, - "https://issuer-should-not-be-called.example.com/", "test-client")) - require.NoError(t, err) - - // Login with explicit issuer/clientID and WithTokenDir (internal) - // pointing to the directory with the existing token. No OIDC - // provider is needed because the existing token is returned. - tok, err := Login(context.Background(), "", - WithIssuer("https://issuer-should-not-be-called.example.com"), - WithClientID("test-client"), - withTokenDir(tokenDir), - ) - require.NoError(t, err) - assert.Equal(t, "existing-access-token", tok.AccessToken) - assert.Equal(t, "existing-refresh-token", tok.RefreshToken) -} - -// TestLogin_ExpiredTokenTriggersFlow verifies that an expired token on -// disk does not short-circuit: Login proceeds to the interactive flow. -// Since we use keyboard flow (no browser), we feed it a code and verify -// a new token is returned. -func TestLogin_ExpiredTokenTriggersFlow(t *testing.T) { - resetDiscoveryCache() - - provider := setupMockProvider(t) - tokenDir := t.TempDir() - - // Write an expired token. - expiredToken := &oauth2.Token{ - AccessToken: "expired-token", - RefreshToken: "old-refresh", - TokenType: "Bearer", - Expiry: time.Now().Add(-1 * time.Hour), // expired - } - err := writeToken(tokenDir, oidctoken.FromToken(expiredToken, provider.URL, "test-client")) - require.NoError(t, err) - - // Start a callback server ourselves to simulate the auth code callback. - // We'll use keyboard flow to avoid browser dependency. - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - // Use keyboard flow with a reader that provides a fake auth code. - // The mock provider's /token endpoint accepts any code. - codeReader := strings.NewReader("fake-auth-code\n") - - tok, err := Login(ctx, "", - WithIssuer(provider.URL), - WithClientID("test-client"), - withTokenDir(tokenDir), - WithKeyboardFlow(), - withInput(codeReader), - ) - require.NoError(t, err) - assert.Equal(t, "login-access-token", tok.AccessToken) - assert.Equal(t, "login-refresh-token", tok.RefreshToken) -} - -// TestLogin_ReplacesMismatchedStoredToken verifies that a valid stored -// token issued for another client or issuer, or written by an earlier Go -// SDK build, is not reused: Login runs the flow and rewrites the file in -// the CLI format. -func TestLogin_ReplacesMismatchedStoredToken(t *testing.T) { - resetDiscoveryCache() - - provider := setupMockProvider(t) - expiresAt := fmt.Sprint(time.Now().Add(time.Hour).Unix()) - expiry := time.Now().Add(time.Hour).UTC().Format(time.RFC3339) - stored := map[string]string{ - "different client ID": `{"access_token": "stored-token", "expires_at": ` + expiresAt + - `, "issuer": "` + provider.URL + `", "client_id": "other-client"}`, - "different issuer": `{"access_token": "stored-token", "expires_at": ` + expiresAt + - `, "issuer": "https://other-issuer.example.com", "client_id": "test-client"}`, - "earlier Go SDK format": `{"access_token": "stored-token", "refresh_token": "stored-refresh", "expiry": "` + - expiry + `", "expires_in": 3600}`, - } - - for name, content := range stored { - t.Run(name, func(t *testing.T) { - tokenDir := t.TempDir() - require.NoError(t, os.WriteFile(filepath.Join(tokenDir, oidctoken.FileName), []byte(content), 0o600)) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - tok, err := Login(ctx, "", - WithIssuer(provider.URL), - WithClientID("test-client"), - withTokenDir(tokenDir), - WithKeyboardFlow(), - withInput(strings.NewReader("auth-code\n")), - ) - require.NoError(t, err) - assert.Equal(t, "login-access-token", tok.AccessToken) - - diskTok, err := readToken(tokenDir) - require.NoError(t, err) - require.NotNil(t, diskTok) - assert.Equal(t, "login-access-token", diskTok.AccessToken) - assert.Equal(t, provider.URL, diskTok.Issuer) - assert.Equal(t, "test-client", diskTok.ClientID) - }) - } -} - -// TestLogin_KeyboardFlow verifies that Login completes using the -// keyboard flow when WithKeyboardFlow() is set. The test provides a -// mock OIDC provider and feeds an auth code through a reader. -func TestLogin_KeyboardFlow(t *testing.T) { - resetDiscoveryCache() - - provider := setupMockProvider(t) - tokenDir := t.TempDir() - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - codeReader := strings.NewReader("keyboard-auth-code\n") - - tok, err := Login(ctx, "", - WithIssuer(provider.URL), - WithClientID("test-client"), - withTokenDir(tokenDir), - WithKeyboardFlow(), - withInput(codeReader), - ) - require.NoError(t, err) - assert.Equal(t, "login-access-token", tok.AccessToken) - - // Verify token was persisted to disk. - diskTok, err := readToken(tokenDir) - require.NoError(t, err) - require.NotNil(t, diskTok) - assert.Equal(t, "login-access-token", diskTok.AccessToken) -} - -// TestLogin_InMemorySkipsPersistence verifies that WithInMemory() -// returns a token without writing to disk. -func TestLogin_InMemorySkipsPersistence(t *testing.T) { - resetDiscoveryCache() - - provider := setupMockProvider(t) - tokenDir := t.TempDir() - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - codeReader := strings.NewReader("some-code\n") - - tok, err := Login(ctx, "", - WithIssuer(provider.URL), - WithClientID("test-client"), - withTokenDir(tokenDir), - WithKeyboardFlow(), - WithInMemory(), - withInput(codeReader), - ) - require.NoError(t, err) - assert.Equal(t, "login-access-token", tok.AccessToken) - - // Verify NO token file on disk. - _, err = os.Stat(filepath.Join(tokenDir, oidctoken.FileName)) - assert.True(t, os.IsNotExist(err), "token file should not exist in in-memory mode") -} - -// TestLogin_DiscoveryFailure verifies that Login returns ErrDiscovery -// when the OIDC provider is unreachable. -func TestLogin_DiscoveryFailure(t *testing.T) { - resetDiscoveryCache() - - // Point to a server that doesn't exist. - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - _, err := Login(ctx, "", - WithIssuer("http://127.0.0.1:1"), // port 1 should refuse connections - WithClientID("test-client"), - WithKeyboardFlow(), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrDiscovery), "expected ErrDiscovery, got: %v", err) -} - -// TestLogin_GatewayResolution verifies that Login resolves OIDC config -// from gateway metadata when a gateway name is provided. We use the -// withGatewayResolver option to inject a fake gateway loader. -func TestLogin_GatewayResolution(t *testing.T) { - resetDiscoveryCache() - - provider := setupMockProvider(t) - tokenDir := t.TempDir() - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - codeReader := strings.NewReader("gw-auth-code\n") - - fakeConfig := &gateway.Config{ - Name: "test-gateway", - Endpoint: "gateway.example.com:443", - Dir: tokenDir, - OIDCIssuer: provider.URL, - OIDCClientID: "gateway-client-id", - } - - tok, err := Login(ctx, "test-gateway", - WithKeyboardFlow(), - withInput(codeReader), - withGatewayResolver(func(name string) (*gateway.Config, error) { - assert.Equal(t, "test-gateway", name) - return fakeConfig, nil - }), - ) - require.NoError(t, err) - assert.Equal(t, "login-access-token", tok.AccessToken) - - // Verify token was persisted in the gateway dir. - diskTok, err := readToken(tokenDir) - require.NoError(t, err) - require.NotNil(t, diskTok) - assert.Equal(t, "login-access-token", diskTok.AccessToken) - assert.Equal(t, provider.URL, diskTok.Issuer) - assert.Equal(t, "gateway-client-id", diskTok.ClientID) -} - -// TestLogin_GatewayMissingOIDCFields verifies that Login returns -// ErrOIDCConfig when the gateway config has empty OIDC fields. -func TestLogin_GatewayMissingOIDCFields(t *testing.T) { - resetDiscoveryCache() - - fakeConfig := &gateway.Config{ - Name: "no-oidc-gw", - Endpoint: "gateway.example.com:443", - Dir: t.TempDir(), - // OIDCIssuer and OIDCClientID are empty. - } - - _, err := Login(context.Background(), "no-oidc-gw", - withGatewayResolver(func(_ string) (*gateway.Config, error) { - return fakeConfig, nil - }), - ) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrOIDCConfig), "expected ErrOIDCConfig, got: %v", err) -} - -// TestLogin_GatewayResolutionError verifies that Login propagates -// errors from gateway resolution. -func TestLogin_GatewayResolutionError(t *testing.T) { - resetDiscoveryCache() - - gwErr := fmt.Errorf("gateway not found: no-such-gateway") - - _, err := Login(context.Background(), "no-such-gateway", - withGatewayResolver(func(_ string) (*gateway.Config, error) { - return nil, gwErr - }), - ) - require.Error(t, err) - assert.Contains(t, err.Error(), "gateway not found") -} - -// TestLogin_NoPKCESupport verifies that Login proceeds without PKCE -// when the OIDC provider does not advertise S256 support. -func TestLogin_NoPKCESupport(t *testing.T) { - resetDiscoveryCache() - - // Create a provider that does NOT list S256 in supported methods. - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "authorization_endpoint": srv.URL + "/authorize", - "token_endpoint": srv.URL + "/token", - "scopes_supported": []string{"openid"}, - // No code_challenge_methods_supported field. - } - _ = json.NewEncoder(w).Encode(doc) - }) - - var receivedVerifier string - mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { - _ = r.ParseForm() - receivedVerifier = r.Form.Get("code_verifier") - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(tokenResponseJSON("no-pkce-token", "", 3600))) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - codeReader := strings.NewReader("some-code\n") - - tok, err := Login(ctx, "", - WithIssuer(srv.URL), - WithClientID("test-client"), - withTokenDir(t.TempDir()), - WithKeyboardFlow(), - withInput(codeReader), - ) - require.NoError(t, err) - assert.Equal(t, "no-pkce-token", tok.AccessToken) - - // Verify no PKCE verifier was sent to the token endpoint. - assert.Empty(t, receivedVerifier, "code_verifier should not be sent when PKCE is not supported") -} - -// TestLogin_ContextCancellation verifies that Login respects context -// cancellation during the interactive flow. -func TestLogin_ContextCancellation(t *testing.T) { - resetDiscoveryCache() - - provider := setupMockProvider(t) - - // Create a context that is already cancelled. - ctx, cancel := context.WithCancel(context.Background()) - cancel() // cancel immediately - - _, err := Login(ctx, "", - WithIssuer(provider.URL), - WithClientID("test-client"), - WithKeyboardFlow(), - ) - require.Error(t, err) - // Should get a context error or timeout error. - assert.True(t, - errors.Is(err, ErrTimeout) || errors.Is(err, ErrDiscovery) || errors.Is(err, context.Canceled), - "expected timeout/discovery/cancelled error, got: %v", err, - ) -} - -// TestLogin_CustomScopes verifies that WithScopes overrides the -// default scopes sent in the authorization request. -func TestLogin_CustomScopes(t *testing.T) { - resetDiscoveryCache() - - // Provider that captures the auth URL scope parameter. - mux := http.NewServeMux() - var srv *httptest.Server - - mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - doc := map[string]any{ - "issuer": srv.URL, - "authorization_endpoint": srv.URL + "/authorize", - "token_endpoint": srv.URL + "/token", - "code_challenge_methods_supported": []string{"S256"}, - } - _ = json.NewEncoder(w).Encode(doc) - }) - - mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { - _ = r.ParseForm() - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(tokenResponseJSON("scoped-token", "", 3600))) - }) - - srv = httptest.NewServer(mux) - t.Cleanup(srv.Close) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - codeReader := strings.NewReader("auth-code\n") - - tok, err := Login(ctx, "", - WithIssuer(srv.URL), - WithClientID("test-client"), - withTokenDir(t.TempDir()), - WithKeyboardFlow(), - WithScopes("openid", "custom-scope"), - withInput(codeReader), - ) - require.NoError(t, err) - assert.Equal(t, "scoped-token", tok.AccessToken) -} - -// TestLoginBrowser_PortBusy_FallbackToKeyboard verifies that -// loginBrowser falls back to keyboard flow when the callback server -// port is already occupied and no custom port is set. -func TestLoginBrowser_PortBusy_FallbackToKeyboard(t *testing.T) { - resetDiscoveryCache() - - provider := setupMockProvider(t) - - // Occupy port 8000 so startCallbackServer fails on the primary port. - // Then occupy port 18000 so the fallback port also fails. - // This forces loginBrowser into the keyboard fallback path. - ln1, err1 := net.Listen("tcp", "127.0.0.1:8000") - ln2, err2 := net.Listen("tcp", "127.0.0.1:18000") - if err1 != nil || err2 != nil { - if ln1 != nil { - _ = ln1.Close() - } - if ln2 != nil { - _ = ln2.Close() - } - t.Skip("Cannot bind test ports 8000 and 18000") - } - defer func() { _ = ln1.Close() }() - defer func() { _ = ln2.Close() }() - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - codeReader := strings.NewReader("keyboard-fallback-code\n") - - tok, err := Login(ctx, "", - WithIssuer(provider.URL), - WithClientID("test-client"), - withTokenDir(t.TempDir()), - withInput(codeReader), - ) - require.NoError(t, err) - assert.Equal(t, "login-access-token", tok.AccessToken) -} - -// TestLogin_ContextTimeout verifies that a Login with a very short -// timeout returns a timeout-related error. -func TestLogin_ContextTimeout(t *testing.T) { - resetDiscoveryCache() - - provider := setupMockProvider(t) - - // Create a context that times out immediately. - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond) - defer cancel() - time.Sleep(1 * time.Millisecond) // ensure timeout fires - - _, err := Login(ctx, "", - WithIssuer(provider.URL), - WithClientID("test-client"), - WithKeyboardFlow(), - ) - require.Error(t, err) -} diff --git a/sdk/go/openshell/v1/oidc/options.go b/sdk/go/openshell/v1/oidc/options.go deleted file mode 100644 index d4d856ba4b..0000000000 --- a/sdk/go/openshell/v1/oidc/options.go +++ /dev/null @@ -1,189 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "context" - "io" - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/gateway" -) - -// defaultScopes are the OIDC scopes requested when no custom scopes -// are specified via [WithScopes]. -var defaultScopes = []string{"openid", "profile", "email"} - -// defaultTimeout is the maximum duration for interactive login flows -// (browser, keyboard, device code) when no custom timeout is set. -const defaultTimeout = 2 * time.Minute - -// loginConfig holds the resolved configuration for a single login -// attempt. It is built by applying [LoginOption] functions to a -// zero-value struct and then filling in defaults. -type loginConfig struct { - issuer string - clientID string - clientSecret string - secretProvider func(context.Context) (string, error) - audience string - audienceSet bool - scopes []string - scopesSet bool - callbackPort int - timeout time.Duration - keyboardFlow bool - inMemory bool - displayFunc func(verificationURL, userCode string) - gateway string - - // Internal fields for testing. Not exposed via public API. - tokenDir string // override token directory - input io.Reader // override stdin for keyboard flow - output io.Writer // override stderr for keyboard flow - gatewayResolver func(name string) (*gateway.Config, error) // override gateway.LoadConfig -} - -// applyDefaults fills in default values for fields that were not set -// by any option function. -func (c *loginConfig) applyDefaults() { - if len(c.scopes) == 0 { - // Deep copy to avoid callers mutating the package-level slice. - c.scopes = make([]string, len(defaultScopes)) - copy(c.scopes, defaultScopes) - } - if c.timeout == 0 { - c.timeout = defaultTimeout - } -} - -// LoginOption configures a login attempt. Use the With* functions to -// create option values. -type LoginOption func(*loginConfig) - -// WithIssuer sets the OIDC issuer URL. Required for standalone flows -// (when no gateway name is provided to [Login]). -func WithIssuer(url string) LoginOption { - return func(c *loginConfig) { - c.issuer = url - } -} - -// WithClientID sets the OAuth2 client ID. Required for standalone -// flows (when no gateway name is provided to [Login]). -func WithClientID(id string) LoginOption { - return func(c *loginConfig) { - c.clientID = id - } -} - -// WithClientSecret sets the client secret for the client credentials -// grant. Required for [ClientCredentials] and [NewClientCredentialsAuth]. -func WithClientSecret(secret string) LoginOption { - return func(c *loginConfig) { - c.clientSecret = secret - } -} - -// WithClientSecretProvider resolves the client secret for each exchange. -// Provider errors and returned secrets are never included in SDK errors. -func WithClientSecretProvider(provider func(context.Context) (string, error)) LoginOption { - return func(c *loginConfig) { - c.secretProvider = provider - } -} - -// WithAudience sets the optional OAuth2 resource-server audience. -func WithAudience(audience string) LoginOption { - return func(c *loginConfig) { - c.audience = audience - c.audienceSet = true - } -} - -// WithScopes overrides the default scopes (openid, profile, email). -// The provided scopes replace the defaults entirely. -func WithScopes(scopes ...string) LoginOption { - return func(c *loginConfig) { - c.scopes = make([]string, len(scopes)) - copy(c.scopes, scopes) - c.scopesSet = true - } -} - -// WithCallbackPort sets a fixed port for the localhost callback server. -// By default the server tries port 8000, then 18000. -func WithCallbackPort(port int) LoginOption { - return func(c *loginConfig) { - c.callbackPort = port - } -} - -// WithTimeout sets the maximum duration for interactive login flows. -// The default is 2 minutes. -func WithTimeout(d time.Duration) LoginOption { - return func(c *loginConfig) { - c.timeout = d - } -} - -// WithKeyboardFlow forces the keyboard flow (manual URL copy and code -// paste) instead of attempting to open a browser. -func WithKeyboardFlow() LoginOption { - return func(c *loginConfig) { - c.keyboardFlow = true - } -} - -// WithInMemory skips persisting the token to disk. The returned token -// is only available in memory for the lifetime of the process. -func WithInMemory() LoginOption { - return func(c *loginConfig) { - c.inMemory = true - } -} - -// WithDisplayFunc sets a custom display function for the device code -// flow. The function receives the verification URL and user code that -// the user must enter to authorize the device. If not set, the default -// behavior prints to stdout. -func WithDisplayFunc(fn func(verificationURL, userCode string)) LoginOption { - return func(c *loginConfig) { - c.displayFunc = fn - } -} - -// WithGateway sets the gateway name for [DeviceLogin], [ClientCredentials], -// and [NewClientCredentialsAuth]. OIDC configuration is read from the -// gateway's metadata.json. Client-credentials tokens are never persisted. -func WithGateway(name string) LoginOption { - return func(c *loginConfig) { - c.gateway = name - } -} - -// --- Internal options for testing (unexported) --- - -// withTokenDir overrides the token directory for testing. -func withTokenDir(dir string) LoginOption { - return func(c *loginConfig) { - c.tokenDir = dir - } -} - -// withInput overrides the input reader for keyboard flow testing. -func withInput(r io.Reader) LoginOption { - return func(c *loginConfig) { - c.input = r - } -} - -// withGatewayResolver overrides the gateway.LoadConfig function for -// testing. This allows tests to inject a fake gateway resolver -// without filesystem setup. -func withGatewayResolver(fn func(name string) (*gateway.Config, error)) LoginOption { - return func(c *loginConfig) { - c.gatewayResolver = fn - } -} diff --git a/sdk/go/openshell/v1/oidc/options_nil_test.go b/sdk/go/openshell/v1/oidc/options_nil_test.go deleted file mode 100644 index 49ec64cf6a..0000000000 --- a/sdk/go/openshell/v1/oidc/options_nil_test.go +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" -) - -func TestLoginOption_NilIgnored(t *testing.T) { - var cfg loginConfig - options.Apply(&cfg, []LoginOption{ - WithIssuer("https://auth.example.com"), - nil, - WithClientID("my-app"), - }) - assert.Equal(t, "https://auth.example.com", cfg.issuer) - assert.Equal(t, "my-app", cfg.clientID) -} diff --git a/sdk/go/openshell/v1/oidc/options_test.go b/sdk/go/openshell/v1/oidc/options_test.go deleted file mode 100644 index 28d17c06b4..0000000000 --- a/sdk/go/openshell/v1/oidc/options_test.go +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestLoginConfig_Defaults(t *testing.T) { - var cfg loginConfig - cfg.applyDefaults() - - assert.Equal(t, []string{"openid", "profile", "email"}, cfg.scopes) - assert.Equal(t, 2*time.Minute, cfg.timeout) - assert.Empty(t, cfg.issuer) - assert.Empty(t, cfg.clientID) - assert.Empty(t, cfg.clientSecret) - assert.Zero(t, cfg.callbackPort) - assert.False(t, cfg.keyboardFlow) - assert.False(t, cfg.inMemory) - assert.Nil(t, cfg.displayFunc) - assert.Empty(t, cfg.gateway) -} - -func TestWithIssuer(t *testing.T) { - var cfg loginConfig - WithIssuer("https://auth.example.com")(&cfg) - - assert.Equal(t, "https://auth.example.com", cfg.issuer) -} - -func TestWithClientID(t *testing.T) { - var cfg loginConfig - WithClientID("my-app")(&cfg) - - assert.Equal(t, "my-app", cfg.clientID) -} - -func TestWithClientSecret(t *testing.T) { - var cfg loginConfig - WithClientSecret("s3cret")(&cfg) - - assert.Equal(t, "s3cret", cfg.clientSecret) -} - -func TestWithScopes(t *testing.T) { - var cfg loginConfig - WithScopes("openid", "custom")(&cfg) - cfg.applyDefaults() - - // Custom scopes should not be overwritten by defaults. - assert.Equal(t, []string{"openid", "custom"}, cfg.scopes) -} - -func TestWithScopes_DeepCopy(t *testing.T) { - original := []string{"openid", "custom"} - var cfg loginConfig - WithScopes(original...)(&cfg) - - // Mutating the original slice should not affect the config. - original[0] = "mutated" - assert.Equal(t, "openid", cfg.scopes[0]) -} - -func TestWithCallbackPort(t *testing.T) { - var cfg loginConfig - WithCallbackPort(9090)(&cfg) - - assert.Equal(t, 9090, cfg.callbackPort) -} - -func TestWithTimeout(t *testing.T) { - var cfg loginConfig - WithTimeout(5 * time.Minute)(&cfg) - cfg.applyDefaults() - - // Custom timeout should not be overwritten by defaults. - assert.Equal(t, 5*time.Minute, cfg.timeout) -} - -func TestWithKeyboardFlow(t *testing.T) { - var cfg loginConfig - WithKeyboardFlow()(&cfg) - - assert.True(t, cfg.keyboardFlow) -} - -func TestWithInMemory(t *testing.T) { - var cfg loginConfig - WithInMemory()(&cfg) - - assert.True(t, cfg.inMemory) -} - -func TestWithDisplayFunc(t *testing.T) { - called := false - fn := func(_, _ string) { called = true } - - var cfg loginConfig - WithDisplayFunc(fn)(&cfg) - - assert.NotNil(t, cfg.displayFunc) - cfg.displayFunc("http://example.com", "ABCD-1234") - assert.True(t, called) -} - -func TestWithGateway(t *testing.T) { - var cfg loginConfig - WithGateway("prod-gw")(&cfg) - - assert.Equal(t, "prod-gw", cfg.gateway) -} - -func TestMultipleOptions(t *testing.T) { - opts := []LoginOption{ - WithIssuer("https://auth.example.com"), - WithClientID("app-id"), - WithScopes("openid"), - WithTimeout(30 * time.Second), - WithKeyboardFlow(), - } - - var cfg loginConfig - for _, opt := range opts { - opt(&cfg) - } - cfg.applyDefaults() - - assert.Equal(t, "https://auth.example.com", cfg.issuer) - assert.Equal(t, "app-id", cfg.clientID) - assert.Equal(t, []string{"openid"}, cfg.scopes) - assert.Equal(t, 30*time.Second, cfg.timeout) - assert.True(t, cfg.keyboardFlow) -} - -func TestDefaultScopes_NotMutatedByConfig(t *testing.T) { - // Verify the package-level defaultScopes slice is not shared. - var cfg loginConfig - cfg.applyDefaults() - cfg.scopes[0] = "mutated" - - assert.Equal(t, "openid", defaultScopes[0]) -} - -func TestLastOptionWins(t *testing.T) { - var cfg loginConfig - WithIssuer("first")(&cfg) - WithIssuer("second")(&cfg) - - assert.Equal(t, "second", cfg.issuer) -} diff --git a/sdk/go/openshell/v1/oidc/token.go b/sdk/go/openshell/v1/oidc/token.go deleted file mode 100644 index 7dd71cba6a..0000000000 --- a/sdk/go/openshell/v1/oidc/token.go +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/oidctoken" -) - -// writeToken persists an OIDC token bundle to disk as oidc_token.json in -// the given directory, in the CLI's format. The file is written with 0600 -// permissions (owner-only) to protect credentials. -func writeToken(dir string, b oidctoken.Bundle) error { - data, err := json.Marshal(b) - if err != nil { - return fmt.Errorf("%w: failed to marshal token: %v", ErrTokenPersist, err) - } - - path := filepath.Join(dir, oidctoken.FileName) - tmp, err := os.CreateTemp(dir, ".oidc-token-*") - if err != nil { - return fmt.Errorf("%w: failed to create temporary token file: %v", ErrTokenPersist, err) - } - tmpPath := tmp.Name() - defer func() { _ = os.Remove(tmpPath) }() - if err := tmp.Chmod(0o600); err != nil { - _ = tmp.Close() - return fmt.Errorf("%w: failed to secure temporary token file: %v", ErrTokenPersist, err) - } - if _, err := tmp.Write(data); err != nil { - _ = tmp.Close() - return fmt.Errorf("%w: failed to write %s: %v", ErrTokenPersist, path, err) - } - if err := tmp.Sync(); err != nil { - _ = tmp.Close() - return fmt.Errorf("%w: failed to sync %s: %v", ErrTokenPersist, path, err) - } - if err := tmp.Close(); err != nil { - return fmt.Errorf("%w: failed to close %s: %v", ErrTokenPersist, path, err) - } - if err := os.Rename(tmpPath, path); err != nil { - return fmt.Errorf("%w: failed to replace %s: %v", ErrTokenPersist, path, err) - } - - return nil -} - -// readToken reads an existing oidc_token.json from the given -// directory. It returns: -// - (bundle, nil) if the file exists and parses -// - (nil, nil) if the file does not exist -// - (nil, error) if the file exists but cannot be read or parsed -func readToken(dir string) (*oidctoken.Bundle, error) { - path := filepath.Join(dir, oidctoken.FileName) - - data, err := os.ReadFile(path) - if err != nil { - if os.IsNotExist(err) { - return nil, nil - } - return nil, fmt.Errorf("%w: cannot read %s: %v", ErrTokenPersist, path, err) - } - - var bundle oidctoken.Bundle - if err := json.Unmarshal(data, &bundle); err != nil { - return nil, fmt.Errorf("%w: invalid JSON in %s: %v", ErrTokenPersist, oidctoken.FileName, err) - } - - return &bundle, nil -} diff --git a/sdk/go/openshell/v1/oidc/token_test.go b/sdk/go/openshell/v1/oidc/token_test.go deleted file mode 100644 index 6558a038bf..0000000000 --- a/sdk/go/openshell/v1/oidc/token_test.go +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package oidc - -import ( - "errors" - "os" - "path/filepath" - "runtime" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "golang.org/x/oauth2" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/oidctoken" -) - -func loadOIDCTokenFixture(t *testing.T) []byte { - t.Helper() - data, err := os.ReadFile(filepath.Join("..", "..", "..", "..", "conformance", "oidc-token-bundle.json")) - require.NoError(t, err) - return data -} - -func TestReadToken_ConformanceFixture(t *testing.T) { - dir := t.TempDir() - require.NoError(t, os.WriteFile(filepath.Join(dir, oidctoken.FileName), loadOIDCTokenFixture(t), 0o600)) - - b, err := readToken(dir) - require.NoError(t, err) - require.NotNil(t, b) - assert.Equal(t, "conformance-access-token", b.AccessToken) - assert.Equal(t, "conformance-refresh-token", b.RefreshToken) - require.NotNil(t, b.ExpiresAt) - assert.Equal(t, uint64(4102444800), *b.ExpiresAt) - assert.Equal(t, "https://issuer.example.com", b.Issuer) - assert.Equal(t, "openshell-cli", b.ClientID) -} - -func TestWriteToken_MatchesConformanceFixture(t *testing.T) { - dir := t.TempDir() - tok := &oauth2.Token{ - AccessToken: "conformance-access-token", - RefreshToken: "conformance-refresh-token", - Expiry: time.Unix(4102444800, 0), - } - - require.NoError(t, writeToken(dir, oidctoken.FromToken(tok, "https://issuer.example.com", "openshell-cli"))) - - data, err := os.ReadFile(filepath.Join(dir, oidctoken.FileName)) - require.NoError(t, err) - assert.JSONEq(t, string(loadOIDCTokenFixture(t)), string(data)) -} - -func TestWriteToken_OmitsEmptyOptionalFields(t *testing.T) { - dir := t.TempDir() - tok := &oauth2.Token{AccessToken: "access-only"} - - require.NoError(t, writeToken(dir, oidctoken.FromToken(tok, "https://issuer.example.com", "openshell-cli"))) - - data, err := os.ReadFile(filepath.Join(dir, oidctoken.FileName)) - require.NoError(t, err) - assert.JSONEq(t, - `{"access_token": "access-only", "issuer": "https://issuer.example.com", "client_id": "openshell-cli"}`, - string(data)) -} - -func TestWriteToken_ReplacesInsecureExistingFileWithOwnerOnlyFile(t *testing.T) { - dir := t.TempDir() - path := filepath.Join(dir, oidctoken.FileName) - require.NoError(t, os.WriteFile(path, []byte("old"), 0o644)) - - require.NoError(t, writeToken(dir, oidctoken.Bundle{AccessToken: "secret"})) - - assertOwnerOnlyFilePermissions(t, path) -} - -func TestWriteToken_InvalidDirectory(t *testing.T) { - err := writeToken("/nonexistent/path/that/does/not/exist", oidctoken.Bundle{AccessToken: "test"}) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrTokenPersist)) -} - -func TestReadToken_MissingFile(t *testing.T) { - dir := t.TempDir() - - tok, err := readToken(dir) - assert.Nil(t, tok) - assert.NoError(t, err, "missing file should return nil token, no error") -} - -func TestReadToken_InvalidJSON(t *testing.T) { - dir := t.TempDir() - err := os.WriteFile(filepath.Join(dir, "oidc_token.json"), []byte(`{invalid`), 0o600) - require.NoError(t, err) - - _, err = readToken(dir) - require.Error(t, err) - assert.True(t, errors.Is(err, ErrTokenPersist)) -} - -func TestWriteToken_FilePermissions(t *testing.T) { - dir := t.TempDir() - - err := writeToken(dir, oidctoken.Bundle{AccessToken: "perm-check"}) - require.NoError(t, err) - - assertOwnerOnlyFilePermissions(t, filepath.Join(dir, "oidc_token.json")) -} - -func assertOwnerOnlyFilePermissions(t *testing.T, path string) { - t.Helper() - if runtime.GOOS == "windows" { - t.Skip("POSIX file permission bits are not supported on Windows") - } - - info, err := os.Stat(path) - require.NoError(t, err) - assert.Equal(t, os.FileMode(0o600), info.Mode().Perm()) -} diff --git a/sdk/go/openshell/v1/options.go b/sdk/go/openshell/v1/options.go deleted file mode 100644 index 5d603552ea..0000000000 --- a/sdk/go/openshell/v1/options.go +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "math" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// CreateOption configures a Create call. -type CreateOption = types.CreateOption - -// WithLabels sets labels on the created resource. -var WithLabels = types.WithLabels - -// WithAnnotations sets annotations on the created resource. -var WithAnnotations = types.WithAnnotations - -// WithServiceExposures registers loopback HTTP services with the created sandbox. -var WithServiceExposures = types.WithServiceExposures - -// ListOptions configures resource listing with pagination and filtering. -type ListOptions = types.ListOptions - -// WatchOptions configures watch behavior. -type WatchOptions = types.WatchOptions - -// WaitOptions configures wait behavior. Use context for timeout control. -type WaitOptions = types.WaitOptions - -// ResumeOptions configures how a suspended sandbox resumes. -type ResumeOptions = types.ResumeOptions - -// ExecOptions configures command execution. -type ExecOptions = types.ExecOptions - -func listPageSize(opts []ListOptions) (int32, error) { - if len(opts) == 0 { - return 0, nil - } - if opts[0].PageSize < 0 { - return 0, &StatusError{Code: ErrorInvalidArgument, Message: "page size must not be negative"} - } - if opts[0].PageSize > math.MaxInt32 { - return math.MaxInt32, nil - } - return int32(opts[0].PageSize), nil -} diff --git a/sdk/go/openshell/v1/options_nil_test.go b/sdk/go/openshell/v1/options_nil_test.go deleted file mode 100644 index 3952bedbf4..0000000000 --- a/sdk/go/openshell/v1/options_nil_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" -) - -func TestRefreshOption_NilIgnored(t *testing.T) { - cfg := defaultRefreshConfig() - options.Apply(&cfg, []RefreshOption{ - WithLeeway(5 * time.Second), - nil, - WithLeeway(20 * time.Second), - }) - assert.Equal(t, 20*time.Second, cfg.leeway) -} - -func TestForwardOption_NilIgnored(t *testing.T) { - var cfg forwardConfig - options.Apply(&cfg, []ForwardOption{ - WithForwardServiceID("svc-a"), - nil, - WithForwardServiceID("svc-b"), - }) - assert.Equal(t, "svc-b", cfg.serviceID) -} - -func TestListenOption_NilIgnored(t *testing.T) { - cfg := listenConfig{bindAddress: "127.0.0.1"} - options.Apply(&cfg, []ListenOption{ - WithBindAddress("0.0.0.0"), - nil, - WithListenServiceID("svc-listen"), - }) - assert.Equal(t, "0.0.0.0", cfg.bindAddress) - assert.Equal(t, "svc-listen", cfg.serviceID) -} - -func TestTunnelOption_NilIgnored(t *testing.T) { - var cfg tunnelConfig - options.Apply(&cfg, []TunnelOption{ - WithTunnelServiceID("svc-x"), - nil, - WithTunnelServiceID("svc-y"), - }) - assert.Equal(t, "svc-y", cfg.serviceID) -} diff --git a/sdk/go/openshell/v1/pager.go b/sdk/go/openshell/v1/pager.go deleted file mode 100644 index a43dfdb957..0000000000 --- a/sdk/go/openshell/v1/pager.go +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "errors" -) - -const ( - maxConsumedPageTokens = 10_000 - maxConsumedPageTokenBytes = 1 << 20 -) - -// Page is one response page from a list operation. -type Page[T any] struct { - Items []T - NextPageToken string -} - -type pageFetcher[T any] func(context.Context, string) (*Page[T], error) - -// Pager lazily fetches pages from the continuation-token contract. -// -// A Pager is single-pass and must not be used concurrently. Its repeated-token -// guard has bounded memory and returns an error if the traversal exceeds its -// token-count or byte budget. -type Pager[T any] struct { - fetch pageFetcher[T] - nextPageToken *string - consumedTokens map[string]struct{} - consumedTokenBytes int - maxConsumedTokens int - maxConsumedTokenBytes int -} - -// NewPager constructs a pager from an RPC page fetcher. -func NewPager[T any](pageToken string, fetch func(context.Context, string) (*Page[T], error)) *Pager[T] { - return &Pager[T]{ - fetch: fetch, - nextPageToken: &pageToken, - consumedTokens: make(map[string]struct{}), - maxConsumedTokens: maxConsumedPageTokens, - maxConsumedTokenBytes: maxConsumedPageTokenBytes, - } -} - -func newPager[T any](pageToken string, fetch pageFetcher[T]) *Pager[T] { - return NewPager(pageToken, fetch) -} - -func (p *Pager[T]) validateCurrentTokenBudget() (int, error) { - if p.nextPageToken == nil || *p.nextPageToken == "" { - return 0, nil - } - tokenBytes := len(*p.nextPageToken) - if len(p.consumedTokens) >= p.maxConsumedTokens || tokenBytes > p.maxConsumedTokenBytes-p.consumedTokenBytes { - return 0, errors.New("pager continuation token history limit exceeded") - } - return tokenBytes, nil -} - -// NextPage fetches the next page. It returns nil after the final page. -func (p *Pager[T]) NextPage(ctx context.Context) (*Page[T], error) { - if p.nextPageToken == nil { - return nil, nil - } - tokenBytes, err := p.validateCurrentTokenBudget() - if err != nil { - return nil, err - } - page, err := p.fetch(ctx, *p.nextPageToken) - if err != nil { - return nil, err - } - if page == nil { - return nil, errors.New("pager fetch returned a nil page") - } - if page.Items == nil { - page.Items = make([]T, 0) - } - if *p.nextPageToken != "" { - p.consumedTokens[*p.nextPageToken] = struct{}{} - p.consumedTokenBytes += tokenBytes - } - if _, seen := p.consumedTokens[page.NextPageToken]; page.NextPageToken != "" && seen { - return nil, errors.New("pager received a repeated continuation token") - } - if page.NextPageToken == "" { - p.nextPageToken = nil - } else { - next := page.NextPageToken - p.nextPageToken = &next - } - return page, nil -} - -// All consumes the pager and collects every remaining item. -func (p *Pager[T]) All(ctx context.Context) ([]T, error) { - items := make([]T, 0) - for { - page, err := p.NextPage(ctx) - if err != nil { - return nil, err - } - if page == nil { - return items, nil - } - items = append(items, page.Items...) - } -} diff --git a/sdk/go/openshell/v1/pager_test.go b/sdk/go/openshell/v1/pager_test.go deleted file mode 100644 index 77d2e3227f..0000000000 --- a/sdk/go/openshell/v1/pager_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "errors" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPagerIsLazyAndFetchesOnePageAtATime(t *testing.T) { - requests := make([]string, 0) - pager := NewPager("resume-token", func(_ context.Context, token string) (*Page[string], error) { - requests = append(requests, token) - if token == "resume-token" { - return &Page[string]{Items: []string{"first"}, NextPageToken: "second-token"}, nil - } - return &Page[string]{Items: []string{"second"}}, nil - }) - - assert.Empty(t, requests) - first, err := pager.NextPage(context.Background()) - require.NoError(t, err) - assert.Equal(t, []string{"first"}, first.Items) - assert.Equal(t, "second-token", first.NextPageToken) - assert.Equal(t, []string{"resume-token"}, requests) - - second, err := pager.NextPage(context.Background()) - require.NoError(t, err) - assert.Equal(t, []string{"second"}, second.Items) - assert.Equal(t, []string{"resume-token", "second-token"}, requests) - - done, err := pager.NextPage(context.Background()) - require.NoError(t, err) - assert.Nil(t, done) -} - -func TestPagerAllRetriesTheCurrentTokenAfterError(t *testing.T) { - wantErr := errors.New("temporary") - attempts := 0 - pager := NewPager("", func(_ context.Context, token string) (*Page[int], error) { - assert.Empty(t, token) - attempts++ - if attempts == 1 { - return nil, wantErr - } - return &Page[int]{Items: []int{1, 2}}, nil - }) - - _, err := pager.NextPage(context.Background()) - assert.ErrorIs(t, err, wantErr) - items, err := pager.All(context.Background()) - require.NoError(t, err) - assert.Equal(t, []int{1, 2}, items) -} - -func TestPagerRejectsRepeatedContinuationToken(t *testing.T) { - pager := NewPager("resume-token", func(_ context.Context, token string) (*Page[string], error) { - return &Page[string]{Items: []string{"first"}, NextPageToken: token}, nil - }) - - page, err := pager.NextPage(context.Background()) - assert.Nil(t, page) - assert.EqualError(t, err, "pager received a repeated continuation token") -} - -func TestPagerBoundsConsumedTokenCount(t *testing.T) { - requests := 0 - pager := NewPager("first", func(_ context.Context, token string) (*Page[string], error) { - requests++ - return &Page[string]{Items: []string{token}, NextPageToken: "next"}, nil - }) - pager.maxConsumedTokens = 1 - - _, err := pager.NextPage(context.Background()) - require.NoError(t, err) - page, err := pager.NextPage(context.Background()) - assert.Nil(t, page) - assert.EqualError(t, err, "pager continuation token history limit exceeded") - assert.Equal(t, 1, requests) -} - -func TestPagerBoundsConsumedTokenBytes(t *testing.T) { - requests := 0 - pager := NewPager("too-large", func(_ context.Context, token string) (*Page[string], error) { - requests++ - return &Page[string]{Items: []string{token}, NextPageToken: "next"}, nil - }) - pager.maxConsumedTokenBytes = 1 - - page, err := pager.NextPage(context.Background()) - assert.Nil(t, page) - assert.EqualError(t, err, "pager continuation token history limit exceeded") - assert.Zero(t, requests) -} - -func TestPagerNormalizesEmptyItems(t *testing.T) { - pager := NewPager("", func(_ context.Context, _ string) (*Page[string], error) { - return &Page[string]{}, nil - }) - - page, err := pager.NextPage(context.Background()) - require.NoError(t, err) - assert.NotNil(t, page.Items) - assert.Empty(t, page.Items) -} - -func TestPagerRejectsNilPage(t *testing.T) { - pager := NewPager("resume-token", func(_ context.Context, token string) (*Page[string], error) { - assert.Equal(t, "resume-token", token) - return nil, nil - }) - - page, err := pager.NextPage(context.Background()) - assert.Nil(t, page) - assert.EqualError(t, err, "pager fetch returned a nil page") -} diff --git a/sdk/go/openshell/v1/policy.go b/sdk/go/openshell/v1/policy.go deleted file mode 100644 index c995eeaf7d..0000000000 --- a/sdk/go/openshell/v1/policy.go +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// SandboxPolicy is the top-level security policy configuration for a sandbox. -type SandboxPolicy = types.SandboxPolicy - -// FilesystemPolicy controls which directories the sandbox can access. -type FilesystemPolicy = types.FilesystemPolicy - -// LandlockPolicy configures the Linux Landlock LSM. -type LandlockPolicy = types.LandlockPolicy - -// ProcessPolicy controls the user and group identity for sandboxed processes. -type ProcessPolicy = types.ProcessPolicy - -// PolicyChunk represents a single proposed policy change in the draft inbox. -type PolicyChunk = types.PolicyChunk - -// DraftChunkApproval binds a bulk approval to an evaluated proposal. -type DraftChunkApproval = types.DraftChunkApproval - -// DraftPolicy contains the full draft policy state returned by GetDraft. -type DraftPolicy = types.DraftPolicy - -// PolicyStatusResult contains the status of a sandbox's policy. -type PolicyStatusResult = types.PolicyStatusResult - -// SandboxPolicyRevision represents a versioned policy revision for a sandbox. -type SandboxPolicyRevision = types.SandboxPolicyRevision - -// PolicyLoadStatus represents the load state of a policy revision. -type PolicyLoadStatus = types.PolicyLoadStatus - -// PolicyLoadStatus constants re-exported from types package. -const ( - PolicyLoadStatusUnspecified = types.PolicyLoadStatusUnspecified - PolicyLoadStatusPending = types.PolicyLoadStatusPending - PolicyLoadStatusLoaded = types.PolicyLoadStatusLoaded - PolicyLoadStatusFailed = types.PolicyLoadStatusFailed - PolicyLoadStatusSuperseded = types.PolicyLoadStatusSuperseded -) - -// ApproveResult contains the result of approving a single draft chunk. -type ApproveResult = types.ApproveResult - -// ApproveAllResult contains the result of approving all draft chunks. -type ApproveAllResult = types.ApproveAllResult - -// UndoResult contains the result of undoing a draft chunk approval. -type UndoResult = types.UndoResult - -// ClearResult contains the result of clearing all draft chunks. -type ClearResult = types.ClearResult - -// DraftHistoryEntry represents a single event in the draft policy history. -type DraftHistoryEntry = types.DraftHistoryEntry - -// GetDraftOption configures a GetDraft call. -type GetDraftOption = types.GetDraftOption - -// WithStatusFilter filters draft chunks by approval status. -var WithStatusFilter = types.WithStatusFilter - -// ApproveAllOption configures an ApproveAllDraftChunks call. -type ApproveAllOption = types.ApproveAllOption - -// WithIncludeSecurityFlagged includes security-flagged chunks in bulk approval. -var WithIncludeSecurityFlagged = types.WithIncludeSecurityFlagged - -// WithDraftApprovals supplies token-bound chunks for bulk approval. -var WithDraftApprovals = types.WithDraftApprovals - -// GetStatusOption configures a GetStatus call. -type GetStatusOption = types.GetStatusOption - -// WithVersion queries a specific policy version instead of the latest. -var WithVersion = types.WithVersion - -// ListPolicyOption configures a List call. -type ListPolicyOption = types.ListPolicyOption - -// WithPageSize sets the maximum revisions requested per page. -var WithPageSize = types.WithPageSize - -// WithPageToken resumes listing from an opaque token returned by a previous page. -var WithPageToken = types.WithPageToken - -// WithListGlobal enables global policy mode on List. When true, the query -// retrieves gateway-global policy revisions instead of sandbox-scoped ones. -var WithListGlobal = types.WithListGlobal - -// WithStatusGlobal enables global policy mode on GetStatus. When true, the -// query retrieves gateway-global policy status instead of sandbox-scoped status. -var WithStatusGlobal = types.WithStatusGlobal - -// PolicyInterface defines operations for managing sandbox policy drafts, -// approvals, and revision history. -type PolicyInterface interface { - GetDraft(ctx context.Context, workspace, sandboxName string, opts ...GetDraftOption) (*DraftPolicy, error) - ApproveDraftChunk(ctx context.Context, workspace, sandboxName, chunkID, reviewToken string) (*ApproveResult, error) - RejectDraftChunk(ctx context.Context, workspace, sandboxName, chunkID, reason string) error - ApproveAllDraftChunks(ctx context.Context, workspace, sandboxName string, opts ...ApproveAllOption) (*ApproveAllResult, error) - ClearDraftChunks(ctx context.Context, workspace, sandboxName string) (*ClearResult, error) - GetDraftHistory(ctx context.Context, workspace, sandboxName string) ([]DraftHistoryEntry, error) - GetStatus(ctx context.Context, workspace, sandboxName string, opts ...GetStatusOption) (*PolicyStatusResult, error) - List(workspace, sandboxName string, opts ...ListPolicyOption) (*Pager[SandboxPolicyRevision], error) - ListAll(ctx context.Context, workspace, sandboxName string, opts ...ListPolicyOption) ([]SandboxPolicyRevision, error) - EditDraftChunk(ctx context.Context, workspace, sandboxName, chunkID string, proposedRule *NetworkPolicyRule) error - UndoDraftChunk(ctx context.Context, workspace, sandboxName, chunkID string) (*UndoResult, error) -} diff --git a/sdk/go/openshell/v1/policy_client.go b/sdk/go/openshell/v1/policy_client.go deleted file mode 100644 index 56fc3c4adb..0000000000 --- a/sdk/go/openshell/v1/policy_client.go +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" -) - -type policyClient struct { - client pb.OpenShellClient -} - -func newPolicyClient(conn grpc.ClientConnInterface) *policyClient { - return &policyClient{client: pb.NewOpenShellClient(conn)} -} - -func (p *policyClient) GetDraft(ctx context.Context, workspace, sandboxName string, opts ...GetDraftOption) (*DraftPolicy, error) { - cfg := types.ApplyGetDraftOptions(opts) - resp, err := p.client.GetDraftPolicy(ctx, &pb.GetDraftPolicyRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - StatusFilter: cfg.StatusFilter(), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.DraftPolicyFromProto(resp), nil -} - -func (p *policyClient) ApproveDraftChunk(ctx context.Context, workspace, sandboxName, chunkID, reviewToken string) (*ApproveResult, error) { - resp, err := p.client.ApproveDraftChunk(ctx, &pb.ApproveDraftChunkRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - ChunkId: chunkID, - ReviewToken: reviewToken, - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.ApproveResultFromProto(resp), nil -} - -func (p *policyClient) RejectDraftChunk(ctx context.Context, workspace, sandboxName, chunkID, reason string) error { - _, err := p.client.RejectDraftChunk(ctx, &pb.RejectDraftChunkRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - ChunkId: chunkID, - Reason: reason, - }) - if err != nil { - return converter.FromGRPCError(err) - } - return nil -} - -func (p *policyClient) ApproveAllDraftChunks(ctx context.Context, workspace, sandboxName string, opts ...ApproveAllOption) (*ApproveAllResult, error) { - cfg := types.ApplyApproveAllOptions(opts) - approvals := make([]*pb.DraftChunkApproval, 0, len(cfg.Approvals())) - for _, approval := range cfg.Approvals() { - approvals = append(approvals, &pb.DraftChunkApproval{ - ChunkId: approval.ChunkID, - ReviewToken: approval.ReviewToken, - }) - } - resp, err := p.client.ApproveAllDraftChunks(ctx, &pb.ApproveAllDraftChunksRequest{ - IncludeSecurityFlagged: cfg.IncludeSecurityFlagged(), - Approvals: approvals, - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.ApproveAllResultFromProto(resp), nil -} - -func (p *policyClient) ClearDraftChunks(ctx context.Context, workspace, sandboxName string) (*ClearResult, error) { - resp, err := p.client.ClearDraftChunks(ctx, &pb.ClearDraftChunksRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.ClearResultFromProto(resp), nil -} - -func (p *policyClient) GetDraftHistory(ctx context.Context, workspace, sandboxName string) ([]DraftHistoryEntry, error) { - resp, err := p.client.GetDraftHistory(ctx, &pb.GetDraftHistoryRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - entries := resp.GetEntries() - if len(entries) == 0 { - return nil, nil - } - result := make([]DraftHistoryEntry, 0, len(entries)) - for _, e := range entries { - if converted := converter.DraftHistoryEntryFromProto(e); converted != nil { - result = append(result, *converted) - } - } - return result, nil -} - -func (p *policyClient) GetStatus(ctx context.Context, workspace, sandboxName string, opts ...GetStatusOption) (*PolicyStatusResult, error) { - cfg := types.ApplyGetStatusOptions(opts) - req := &pb.GetSandboxPolicyStatusRequest{ - Version: cfg.Version(), - Global: cfg.Global(), - } - if !cfg.Global() { - req.Sandbox = sandboxName - req.WorkspaceScope = namedWorkspaceScope(workspace) - } - resp, err := p.client.GetSandboxPolicyStatus(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.PolicyStatusResultFromProto(resp), nil -} - -func (p *policyClient) List(workspace, sandboxName string, opts ...ListPolicyOption) (*Pager[SandboxPolicyRevision], error) { - cfg := types.ApplyListPolicyOptions(opts) - if cfg.PageSize() < 0 { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "page size must not be negative"} - } - if !cfg.Global() && sandboxName == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - return newPager(cfg.PageToken(), func(ctx context.Context, pageToken string) (*Page[SandboxPolicyRevision], error) { - req := &pb.ListSandboxPoliciesRequest{ - Sandbox: sandboxName, PageSize: cfg.PageSize(), PageToken: pageToken, Global: cfg.Global(), - } - if !cfg.Global() { - req.WorkspaceScope = namedWorkspaceScope(workspace) - } - resp, err := p.client.ListSandboxPolicies(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - result := make([]SandboxPolicyRevision, 0, len(resp.GetRevisions())) - for _, revision := range resp.GetRevisions() { - if converted := converter.SandboxPolicyRevisionFromProto(revision); converted != nil { - result = append(result, *converted) - } - } - return &Page[SandboxPolicyRevision]{Items: result, NextPageToken: resp.GetNextPageToken()}, nil - }), nil -} - -func (p *policyClient) ListAll(ctx context.Context, workspace, sandboxName string, opts ...ListPolicyOption) ([]SandboxPolicyRevision, error) { - pager, err := p.List(workspace, sandboxName, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -func (p *policyClient) EditDraftChunk(ctx context.Context, workspace, sandboxName, chunkID string, proposedRule *NetworkPolicyRule) error { - _, err := p.client.EditDraftChunk(ctx, &pb.EditDraftChunkRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - ChunkId: chunkID, - ProposedRule: converter.NetworkPolicyRuleToProto(proposedRule), - }) - if err != nil { - return converter.FromGRPCError(err) - } - return nil -} - -func (p *policyClient) UndoDraftChunk(ctx context.Context, workspace, sandboxName, chunkID string) (*UndoResult, error) { - resp, err := p.client.UndoDraftChunk(ctx, &pb.UndoDraftChunkRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - ChunkId: chunkID, - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.UndoResultFromProto(resp), nil -} diff --git a/sdk/go/openshell/v1/policy_client_test.go b/sdk/go/openshell/v1/policy_client_test.go deleted file mode 100644 index fb6e963b90..0000000000 --- a/sdk/go/openshell/v1/policy_client_test.go +++ /dev/null @@ -1,1033 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "net" - "sync" - "testing" - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - sbv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" - "google.golang.org/protobuf/types/known/timestamppb" -) - -func testTimestamp(milliseconds int64) *timestamppb.Timestamp { - return timestamppb.New(time.UnixMilli(milliseconds)) -} - -// --- Mock server for Policy RPCs --- - -type mockPolicyServer struct { - pb.UnimplementedOpenShellServer - mu sync.Mutex - - // Canned responses. - getDraftResp *pb.GetDraftPolicyResponse - approveResp *pb.ApproveDraftChunkResponse - rejectResp *pb.RejectDraftChunkResponse - approveAllResp *pb.ApproveAllDraftChunksResponse - clearResp *pb.ClearDraftChunksResponse - historyResp *pb.GetDraftHistoryResponse - statusResp *pb.GetSandboxPolicyStatusResponse - listResp *pb.ListSandboxPoliciesResponse - editResp *pb.EditDraftChunkResponse - undoResp *pb.UndoDraftChunkResponse - - // Recorded requests. - lastGetDraftReq *pb.GetDraftPolicyRequest - lastApproveReq *pb.ApproveDraftChunkRequest - lastRejectReq *pb.RejectDraftChunkRequest - lastApproveAllReq *pb.ApproveAllDraftChunksRequest - lastClearReq *pb.ClearDraftChunksRequest - lastHistoryReq *pb.GetDraftHistoryRequest - lastStatusReq *pb.GetSandboxPolicyStatusRequest - lastListReq *pb.ListSandboxPoliciesRequest - lastEditReq *pb.EditDraftChunkRequest - lastUndoReq *pb.UndoDraftChunkRequest - - // Inject errors. - getDraftErr error - approveErr error - rejectErr error - approveAllErr error - clearErr error - historyErr error - statusErr error - listErr error - editErr error - undoErr error -} - -func newMockPolicyServer() *mockPolicyServer { - return &mockPolicyServer{} -} - -func (s *mockPolicyServer) GetDraftPolicy(_ context.Context, req *pb.GetDraftPolicyRequest) (*pb.GetDraftPolicyResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastGetDraftReq = req - if s.getDraftErr != nil { - return nil, s.getDraftErr - } - return s.getDraftResp, nil -} - -func (s *mockPolicyServer) ApproveDraftChunk(_ context.Context, req *pb.ApproveDraftChunkRequest) (*pb.ApproveDraftChunkResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastApproveReq = req - if s.approveErr != nil { - return nil, s.approveErr - } - return s.approveResp, nil -} - -func (s *mockPolicyServer) RejectDraftChunk(_ context.Context, req *pb.RejectDraftChunkRequest) (*pb.RejectDraftChunkResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastRejectReq = req - if s.rejectErr != nil { - return nil, s.rejectErr - } - return s.rejectResp, nil -} - -func (s *mockPolicyServer) ApproveAllDraftChunks(_ context.Context, req *pb.ApproveAllDraftChunksRequest) (*pb.ApproveAllDraftChunksResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastApproveAllReq = req - if s.approveAllErr != nil { - return nil, s.approveAllErr - } - return s.approveAllResp, nil -} - -func (s *mockPolicyServer) ClearDraftChunks(_ context.Context, req *pb.ClearDraftChunksRequest) (*pb.ClearDraftChunksResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastClearReq = req - if s.clearErr != nil { - return nil, s.clearErr - } - return s.clearResp, nil -} - -func (s *mockPolicyServer) GetDraftHistory(_ context.Context, req *pb.GetDraftHistoryRequest) (*pb.GetDraftHistoryResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastHistoryReq = req - if s.historyErr != nil { - return nil, s.historyErr - } - return s.historyResp, nil -} - -func (s *mockPolicyServer) GetSandboxPolicyStatus(_ context.Context, req *pb.GetSandboxPolicyStatusRequest) (*pb.GetSandboxPolicyStatusResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastStatusReq = req - if s.statusErr != nil { - return nil, s.statusErr - } - return s.statusResp, nil -} - -func (s *mockPolicyServer) ListSandboxPolicies(_ context.Context, req *pb.ListSandboxPoliciesRequest) (*pb.ListSandboxPoliciesResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastListReq = req - if s.listErr != nil { - return nil, s.listErr - } - return s.listResp, nil -} - -func (s *mockPolicyServer) EditDraftChunk(_ context.Context, req *pb.EditDraftChunkRequest) (*pb.EditDraftChunkResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastEditReq = req - if s.editErr != nil { - return nil, s.editErr - } - return s.editResp, nil -} - -func (s *mockPolicyServer) UndoDraftChunk(_ context.Context, req *pb.UndoDraftChunkRequest) (*pb.UndoDraftChunkResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastUndoReq = req - if s.undoErr != nil { - return nil, s.undoErr - } - return s.undoResp, nil -} - -// --- Test setup --- - -func setupPolicyTest(t *testing.T, mock *mockPolicyServer) (*policyClient, func()) { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - - return newPolicyClient(conn), func() { - _ = conn.Close() - srv.Stop() - } -} - -// =========================================================================== -// Phase 2 (T020): GetDraft, ApproveDraftChunk, RejectDraftChunk -// =========================================================================== - -func TestPolicyGetDraft(t *testing.T) { - mock := newMockPolicyServer() - mock.getDraftResp = &pb.GetDraftPolicyResponse{ - Chunks: []*pb.PolicyChunk{ - { - Id: "chunk-1", - Status: "pending", - RuleName: "allow-dns", - Rationale: "DNS access needed", - Confidence: 0.95, - DenialSummaryIds: []string{"ds-1", "ds-2"}, - CreatedTime: testTimestamp(1700000000000), - Stage: "initial", - HitCount: 3, - Binary: "/usr/bin/curl", - ProposedRule: &sbv1.NetworkPolicyRule{ - Name: "allow-dns-rule", - }, - }, - { - Id: "chunk-2", - Status: "approved", - RuleName: "allow-https", - }, - }, - RollingSummary: "Two rules proposed", - DraftVersion: 5, - LastAnalyzedTime: testTimestamp(1700000001000), - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - draft, err := client.GetDraft(context.Background(), "default", "my-sandbox") - - require.NoError(t, err) - require.NotNil(t, draft) - - // Verify request was forwarded. - mock.mu.Lock() - assert.Equal(t, "my-sandbox", mock.lastGetDraftReq.GetSandbox()) - assert.Empty(t, mock.lastGetDraftReq.GetStatusFilter()) - mock.mu.Unlock() - - // Verify response mapping. - assert.Equal(t, "Two rules proposed", draft.RollingSummary) - assert.Equal(t, uint64(5), draft.DraftVersion) - assert.False(t, draft.LastAnalyzedAt.IsZero()) - - require.Len(t, draft.Chunks, 2) - - c1 := draft.Chunks[0] - assert.Equal(t, "chunk-1", c1.ID) - assert.Equal(t, "pending", c1.Status) - assert.Equal(t, "allow-dns", c1.RuleName) - assert.Equal(t, "DNS access needed", c1.Rationale) - assert.InDelta(t, float32(0.95), c1.Confidence, 0.001) - assert.Equal(t, []string{"ds-1", "ds-2"}, c1.DenialSummaryIDs) - assert.Equal(t, "initial", c1.Stage) - assert.Equal(t, int32(3), c1.HitCount) - assert.Equal(t, "/usr/bin/curl", c1.Binary) - require.NotNil(t, c1.ProposedRule) - assert.Equal(t, "allow-dns-rule", c1.ProposedRule.Name) - - c2 := draft.Chunks[1] - assert.Equal(t, "chunk-2", c2.ID) - assert.Equal(t, "approved", c2.Status) -} - -func TestPolicyGetDraft_WithStatusFilter(t *testing.T) { - mock := newMockPolicyServer() - mock.getDraftResp = &pb.GetDraftPolicyResponse{ - Chunks: []*pb.PolicyChunk{ - {Id: "chunk-1", Status: "pending"}, - }, - DraftVersion: 3, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - draft, err := client.GetDraft(context.Background(), "default", "sb1", types.WithStatusFilter("pending")) - - require.NoError(t, err) - require.NotNil(t, draft) - - // Verify status filter was forwarded. - mock.mu.Lock() - assert.Equal(t, "pending", mock.lastGetDraftReq.GetStatusFilter()) - mock.mu.Unlock() - - require.Len(t, draft.Chunks, 1) - assert.Equal(t, "pending", draft.Chunks[0].Status) -} - -func TestPolicyGetDraft_Error(t *testing.T) { - mock := newMockPolicyServer() - mock.getDraftErr = status.Errorf(codes.NotFound, "sandbox not found") - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - draft, err := client.GetDraft(context.Background(), "default", "missing") - - assert.Nil(t, draft) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestPolicyApproveDraftChunk(t *testing.T) { - mock := newMockPolicyServer() - mock.approveResp = &pb.ApproveDraftChunkResponse{ - PolicyVersion: 7, - PolicyHash: "sha256:abc123", - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.ApproveDraftChunk(context.Background(), "default", "my-sandbox", "chunk-1", "token-1") - - require.NoError(t, err) - require.NotNil(t, result) - - // Verify request was forwarded. - mock.mu.Lock() - assert.Equal(t, "my-sandbox", mock.lastApproveReq.GetSandbox()) - assert.Equal(t, "chunk-1", mock.lastApproveReq.GetChunkId()) - assert.Equal(t, "token-1", mock.lastApproveReq.GetReviewToken()) - mock.mu.Unlock() - - // Verify response mapping. - assert.Equal(t, uint32(7), result.PolicyVersion) - assert.Equal(t, "sha256:abc123", result.PolicyHash) -} - -func TestPolicyApproveDraftChunk_Error(t *testing.T) { - mock := newMockPolicyServer() - mock.approveErr = status.Errorf(codes.NotFound, "chunk not found") - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.ApproveDraftChunk(context.Background(), "default", "sb1", "bad-chunk", "token-bad") - - assert.Nil(t, result) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestPolicyRejectDraftChunk(t *testing.T) { - mock := newMockPolicyServer() - mock.rejectResp = &pb.RejectDraftChunkResponse{} - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - err := client.RejectDraftChunk(context.Background(), "default", "my-sandbox", "chunk-2", "too broad") - - require.NoError(t, err) - - // Verify request was forwarded. - mock.mu.Lock() - assert.Equal(t, "my-sandbox", mock.lastRejectReq.GetSandbox()) - assert.Equal(t, "chunk-2", mock.lastRejectReq.GetChunkId()) - assert.Equal(t, "too broad", mock.lastRejectReq.GetReason()) - mock.mu.Unlock() -} - -func TestPolicyRejectDraftChunk_Error(t *testing.T) { - mock := newMockPolicyServer() - mock.rejectErr = status.Errorf(codes.InvalidArgument, "invalid chunk") - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - err := client.RejectDraftChunk(context.Background(), "default", "sb1", "bad", "reason") - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -// =========================================================================== -// Phase 3 (T022): ApproveAllDraftChunks, ClearDraftChunks, GetDraftHistory -// =========================================================================== - -func TestPolicyApproveAllDraftChunks(t *testing.T) { - mock := newMockPolicyServer() - mock.approveAllResp = &pb.ApproveAllDraftChunksResponse{ - PolicyVersion: 8, - PolicyHash: "sha256:bulk", - ChunksApproved: 5, - ChunksSkipped: 2, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.ApproveAllDraftChunks(context.Background(), "default", "my-sandbox") - - require.NoError(t, err) - require.NotNil(t, result) - - // Verify default: security-flagged NOT included. - mock.mu.Lock() - assert.Equal(t, "my-sandbox", mock.lastApproveAllReq.GetSandbox()) - assert.False(t, mock.lastApproveAllReq.GetIncludeSecurityFlagged()) - mock.mu.Unlock() - - assert.Equal(t, uint32(8), result.PolicyVersion) - assert.Equal(t, "sha256:bulk", result.PolicyHash) - assert.Equal(t, uint32(5), result.ChunksApproved) - assert.Equal(t, uint32(2), result.ChunksSkipped) -} - -func TestPolicyApproveAllDraftChunks_WithSecurityFlagged(t *testing.T) { - mock := newMockPolicyServer() - mock.approveAllResp = &pb.ApproveAllDraftChunksResponse{ - PolicyVersion: 9, - PolicyHash: "sha256:all", - ChunksApproved: 7, - ChunksSkipped: 0, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.ApproveAllDraftChunks(context.Background(), "default", "sb1", types.WithIncludeSecurityFlagged()) - - require.NoError(t, err) - require.NotNil(t, result) - - // Verify security-flagged flag was sent. - mock.mu.Lock() - assert.True(t, mock.lastApproveAllReq.GetIncludeSecurityFlagged()) - mock.mu.Unlock() - - assert.Equal(t, uint32(7), result.ChunksApproved) - assert.Equal(t, uint32(0), result.ChunksSkipped) -} - -func TestPolicyApproveAllDraftChunks_Error(t *testing.T) { - mock := newMockPolicyServer() - mock.approveAllErr = status.Errorf(codes.NotFound, "sandbox not found") - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.ApproveAllDraftChunks(context.Background(), "default", "missing") - - assert.Nil(t, result) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestPolicyClearDraftChunks(t *testing.T) { - mock := newMockPolicyServer() - mock.clearResp = &pb.ClearDraftChunksResponse{ - ChunksCleared: 4, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.ClearDraftChunks(context.Background(), "default", "my-sandbox") - - require.NoError(t, err) - require.NotNil(t, result) - - // Verify request was forwarded. - mock.mu.Lock() - assert.Equal(t, "my-sandbox", mock.lastClearReq.GetSandbox()) - mock.mu.Unlock() - - assert.Equal(t, uint32(4), result.ChunksCleared) -} - -func TestPolicyClearDraftChunks_Error(t *testing.T) { - mock := newMockPolicyServer() - mock.clearErr = status.Errorf(codes.Internal, "internal error") - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.ClearDraftChunks(context.Background(), "default", "sb1") - - assert.Nil(t, result) - require.Error(t, err) - var se *StatusError - require.ErrorAs(t, err, &se) - assert.Equal(t, ErrorInternal, se.Code) -} - -func TestPolicyGetDraftHistory(t *testing.T) { - mock := newMockPolicyServer() - mock.historyResp = &pb.GetDraftHistoryResponse{ - Entries: []*pb.DraftHistoryEntry{ - { - EventTime: testTimestamp(1700000000000), - EventType: "approved", - Description: "Chunk chunk-1 approved", - ChunkId: "chunk-1", - }, - { - EventTime: testTimestamp(1700000001000), - EventType: "rejected", - Description: "Chunk chunk-2 rejected: too broad", - ChunkId: "chunk-2", - }, - }, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - entries, err := client.GetDraftHistory(context.Background(), "default", "my-sandbox") - - require.NoError(t, err) - require.Len(t, entries, 2) - - // Verify request was forwarded. - mock.mu.Lock() - assert.Equal(t, "my-sandbox", mock.lastHistoryReq.GetSandbox()) - mock.mu.Unlock() - - assert.Equal(t, "approved", entries[0].EventType) - assert.Equal(t, "Chunk chunk-1 approved", entries[0].Description) - assert.Equal(t, "chunk-1", entries[0].ChunkID) - assert.False(t, entries[0].Timestamp.IsZero()) - - assert.Equal(t, "rejected", entries[1].EventType) - assert.Equal(t, "chunk-2", entries[1].ChunkID) -} - -func TestPolicyGetDraftHistory_Empty(t *testing.T) { - mock := newMockPolicyServer() - mock.historyResp = &pb.GetDraftHistoryResponse{} - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - entries, err := client.GetDraftHistory(context.Background(), "default", "sb1") - - require.NoError(t, err) - assert.Nil(t, entries) -} - -func TestPolicyGetDraftHistory_Error(t *testing.T) { - mock := newMockPolicyServer() - mock.historyErr = status.Errorf(codes.NotFound, "sandbox not found") - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - entries, err := client.GetDraftHistory(context.Background(), "default", "missing") - - assert.Nil(t, entries) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -// =========================================================================== -// Phase 4 (T024): GetStatus, List, EditDraftChunk, UndoDraftChunk -// =========================================================================== - -func TestPolicyGetStatus(t *testing.T) { - mock := newMockPolicyServer() - mock.statusResp = &pb.GetSandboxPolicyStatusResponse{ - Revision: &pb.SandboxPolicyRevision{ - Version: 3, - PolicyHash: "sha256:rev3", - Status: pb.PolicyStatus_POLICY_STATUS_LOADED, - CreatedTime: testTimestamp(1700000000000), - LoadedTime: testTimestamp(1700000001000), - }, - ActiveVersion: 3, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.GetStatus(context.Background(), "default", "my-sandbox") - - require.NoError(t, err) - require.NotNil(t, result) - - // Verify request was forwarded (no version = latest). - mock.mu.Lock() - assert.Equal(t, "my-sandbox", mock.lastStatusReq.GetSandbox()) - assert.Equal(t, uint32(0), mock.lastStatusReq.GetVersion()) - mock.mu.Unlock() - - assert.Equal(t, uint32(3), result.ActiveVersion) - assert.Equal(t, uint32(3), result.Revision.Version) - assert.Equal(t, "sha256:rev3", result.Revision.PolicyHash) - assert.Equal(t, PolicyLoadStatusLoaded, result.Revision.Status) - assert.False(t, result.Revision.CreatedAt.IsZero()) - assert.False(t, result.Revision.LoadedAt.IsZero()) -} - -func TestPolicyGetStatus_WithVersion(t *testing.T) { - mock := newMockPolicyServer() - mock.statusResp = &pb.GetSandboxPolicyStatusResponse{ - Revision: &pb.SandboxPolicyRevision{ - Version: 2, - PolicyHash: "sha256:rev2", - Status: pb.PolicyStatus_POLICY_STATUS_SUPERSEDED, - }, - ActiveVersion: 3, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.GetStatus(context.Background(), "default", "sb1", types.WithVersion(2)) - - require.NoError(t, err) - require.NotNil(t, result) - - // Verify version was forwarded. - mock.mu.Lock() - assert.Equal(t, uint32(2), mock.lastStatusReq.GetVersion()) - mock.mu.Unlock() - - assert.Equal(t, uint32(2), result.Revision.Version) - assert.Equal(t, PolicyLoadStatusSuperseded, result.Revision.Status) - assert.Equal(t, uint32(3), result.ActiveVersion) -} - -func TestPolicyGetStatus_WithGlobal(t *testing.T) { - mock := newMockPolicyServer() - mock.statusResp = &pb.GetSandboxPolicyStatusResponse{ - Revision: &pb.SandboxPolicyRevision{ - Version: 1, - PolicyHash: "sha256:global-rev1", - Status: pb.PolicyStatus_POLICY_STATUS_LOADED, - }, - ActiveVersion: 1, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - // GetStatus with global flag and empty name/workspace. - result, err := client.GetStatus(context.Background(), "", "", types.WithStatusGlobal(true)) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, uint32(1), result.Revision.Version) - assert.Equal(t, "sha256:global-rev1", result.Revision.PolicyHash) - - // Verify global flag was forwarded in the proto request. - mock.mu.Lock() - assert.True(t, mock.lastStatusReq.GetGlobal()) - assert.Empty(t, mock.lastStatusReq.GetSandbox()) - mock.mu.Unlock() -} - -func TestPolicyGetStatus_WithGlobalIgnoresNonEmptyName(t *testing.T) { - mock := newMockPolicyServer() - mock.statusResp = &pb.GetSandboxPolicyStatusResponse{ - Revision: &pb.SandboxPolicyRevision{ - Version: 1, - PolicyHash: "sha256:global-rev1", - Status: pb.PolicyStatus_POLICY_STATUS_LOADED, - }, - ActiveVersion: 1, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.GetStatus(context.Background(), "some-workspace", "some-sandbox", types.WithStatusGlobal(true)) - - require.NoError(t, err) - require.NotNil(t, result) - - mock.mu.Lock() - assert.True(t, mock.lastStatusReq.GetGlobal()) - assert.Empty(t, mock.lastStatusReq.GetSandbox()) - mock.mu.Unlock() -} - -func TestPolicyGetStatus_WithGlobalAndVersion(t *testing.T) { - mock := newMockPolicyServer() - mock.statusResp = &pb.GetSandboxPolicyStatusResponse{ - Revision: &pb.SandboxPolicyRevision{ - Version: 3, - PolicyHash: "sha256:global-rev3", - Status: pb.PolicyStatus_POLICY_STATUS_SUPERSEDED, - }, - ActiveVersion: 5, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - // Global flag composes with WithVersion. - result, err := client.GetStatus(context.Background(), "", "", - types.WithStatusGlobal(true), - types.WithVersion(3), - ) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, uint32(3), result.Revision.Version) - assert.Equal(t, uint32(5), result.ActiveVersion) - - mock.mu.Lock() - assert.True(t, mock.lastStatusReq.GetGlobal()) - assert.Equal(t, uint32(3), mock.lastStatusReq.GetVersion()) - mock.mu.Unlock() -} - -func TestPolicyGetStatus_WithoutGlobal_PreservesExistingBehavior(t *testing.T) { - mock := newMockPolicyServer() - mock.statusResp = &pb.GetSandboxPolicyStatusResponse{ - Revision: &pb.SandboxPolicyRevision{ - Version: 1, - Status: pb.PolicyStatus_POLICY_STATUS_LOADED, - }, - ActiveVersion: 1, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - _, err := client.GetStatus(context.Background(), "default", "my-sandbox") - - require.NoError(t, err) - - // Verify global flag is false by default. - mock.mu.Lock() - assert.False(t, mock.lastStatusReq.GetGlobal()) - assert.Equal(t, "default", mock.lastStatusReq.GetWorkspaceScope().GetWorkspace()) - assert.Equal(t, "my-sandbox", mock.lastStatusReq.GetSandbox()) - mock.mu.Unlock() -} - -func TestPolicyGetStatus_Error(t *testing.T) { - mock := newMockPolicyServer() - mock.statusErr = status.Errorf(codes.NotFound, "sandbox not found") - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.GetStatus(context.Background(), "default", "missing") - - assert.Nil(t, result) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestPolicyList(t *testing.T) { - mock := newMockPolicyServer() - mock.listResp = &pb.ListSandboxPoliciesResponse{ - Revisions: []*pb.SandboxPolicyRevision{ - { - Version: 1, - PolicyHash: "sha256:v1", - Status: pb.PolicyStatus_POLICY_STATUS_SUPERSEDED, - CreatedTime: testTimestamp(1700000000000), - }, - { - Version: 2, - PolicyHash: "sha256:v2", - Status: pb.PolicyStatus_POLICY_STATUS_LOADED, - CreatedTime: testTimestamp(1700000001000), - LoadedTime: testTimestamp(1700000002000), - }, - }, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - revisions, err := client.ListAll(context.Background(), "default", "my-sandbox") - - require.NoError(t, err) - require.Len(t, revisions, 2) - - // Verify request was forwarded (no pagination options). - mock.mu.Lock() - assert.Equal(t, "my-sandbox", mock.lastListReq.GetSandbox()) - assert.Equal(t, "default", mock.lastListReq.GetWorkspaceScope().GetWorkspace()) - assert.Equal(t, int32(0), mock.lastListReq.GetPageSize()) - assert.Empty(t, mock.lastListReq.GetPageToken()) - mock.mu.Unlock() - - assert.Equal(t, uint32(1), revisions[0].Version) - assert.Equal(t, "sha256:v1", revisions[0].PolicyHash) - assert.Equal(t, PolicyLoadStatusSuperseded, revisions[0].Status) - - assert.Equal(t, uint32(2), revisions[1].Version) - assert.Equal(t, "sha256:v2", revisions[1].PolicyHash) - assert.Equal(t, PolicyLoadStatusLoaded, revisions[1].Status) -} - -func TestPolicyList_WithPageSize(t *testing.T) { - mock := newMockPolicyServer() - mock.listResp = &pb.ListSandboxPoliciesResponse{ - Revisions: []*pb.SandboxPolicyRevision{ - {Version: 3, PolicyHash: "sha256:v3"}, - }, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - revisions, err := client.ListAll(context.Background(), "default", "my-sandbox", - types.WithPageSize(10), - ) - - require.NoError(t, err) - require.Len(t, revisions, 1) - - // Verify page size was forwarded and the initial token is empty. - mock.mu.Lock() - assert.Equal(t, int32(10), mock.lastListReq.GetPageSize()) - assert.Empty(t, mock.lastListReq.GetPageToken()) - mock.mu.Unlock() -} - -func TestPolicyList_Empty(t *testing.T) { - mock := newMockPolicyServer() - mock.listResp = &pb.ListSandboxPoliciesResponse{} - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - revisions, err := client.ListAll(context.Background(), "default", "my-sandbox") - - require.NoError(t, err) - assert.NotNil(t, revisions) - assert.Empty(t, revisions) -} - -func TestPolicyList_WithGlobal(t *testing.T) { - mock := newMockPolicyServer() - mock.listResp = &pb.ListSandboxPoliciesResponse{ - Revisions: []*pb.SandboxPolicyRevision{ - {Version: 1, PolicyHash: "sha256:global-v1"}, - }, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - // List with global flag and empty workspace. - revisions, err := client.ListAll(context.Background(), "", "", types.WithListGlobal(true)) - - require.NoError(t, err) - require.Len(t, revisions, 1) - assert.Equal(t, uint32(1), revisions[0].Version) - assert.Equal(t, "sha256:global-v1", revisions[0].PolicyHash) - - // Verify global flag was forwarded in the proto request. - mock.mu.Lock() - assert.True(t, mock.lastListReq.GetGlobal()) - assert.Empty(t, mock.lastListReq.GetSandbox()) - mock.mu.Unlock() -} - -func TestPolicyList_WithGlobalIgnoresWorkspace(t *testing.T) { - mock := newMockPolicyServer() - mock.listResp = &pb.ListSandboxPoliciesResponse{ - Revisions: []*pb.SandboxPolicyRevision{ - {Version: 1, PolicyHash: "sha256:global-v1"}, - }, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - revisions, err := client.ListAll(context.Background(), "some-workspace", "", types.WithListGlobal(true)) - - require.NoError(t, err) - require.Len(t, revisions, 1) - - mock.mu.Lock() - assert.True(t, mock.lastListReq.GetGlobal()) - assert.Empty(t, mock.lastListReq.GetSandbox()) - mock.mu.Unlock() -} - -func TestPolicyList_WithGlobalAndPagination(t *testing.T) { - mock := newMockPolicyServer() - mock.listResp = &pb.ListSandboxPoliciesResponse{ - Revisions: []*pb.SandboxPolicyRevision{ - {Version: 5, PolicyHash: "sha256:global-v5"}, - }, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - // Global flag composes with page-size options. - revisions, err := client.ListAll(context.Background(), "", "", - types.WithListGlobal(true), - types.WithPageSize(10), - ) - - require.NoError(t, err) - require.Len(t, revisions, 1) - - mock.mu.Lock() - assert.True(t, mock.lastListReq.GetGlobal()) - assert.Equal(t, int32(10), mock.lastListReq.GetPageSize()) - assert.Empty(t, mock.lastListReq.GetPageToken()) - mock.mu.Unlock() -} - -func TestPolicyList_WithoutGlobal_PreservesExistingBehavior(t *testing.T) { - mock := newMockPolicyServer() - mock.listResp = &pb.ListSandboxPoliciesResponse{ - Revisions: []*pb.SandboxPolicyRevision{ - {Version: 1, PolicyHash: "sha256:v1"}, - }, - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - revisions, err := client.ListAll(context.Background(), "default", "my-sandbox") - - require.NoError(t, err) - require.Len(t, revisions, 1) - - // Verify global flag is false by default. - mock.mu.Lock() - assert.False(t, mock.lastListReq.GetGlobal()) - assert.Equal(t, "my-sandbox", mock.lastListReq.GetSandbox()) - assert.Equal(t, "default", mock.lastListReq.GetWorkspaceScope().GetWorkspace()) - mock.mu.Unlock() -} - -func TestPolicyList_Error(t *testing.T) { - mock := newMockPolicyServer() - mock.listErr = status.Errorf(codes.NotFound, "sandbox not found") - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - revisions, err := client.ListAll(context.Background(), "default", "my-sandbox") - - assert.Nil(t, revisions) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestPolicyEditDraftChunk(t *testing.T) { - mock := newMockPolicyServer() - mock.editResp = &pb.EditDraftChunkResponse{} - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - rule := &NetworkPolicyRule{ - Name: "allow-https", - Endpoints: []PolicyNetworkEndpoint{ - {Host: "example.com", Port: 443, Protocol: "tcp"}, - }, - } - - err := client.EditDraftChunk(context.Background(), "default", "my-sandbox", "chunk-1", rule) - - require.NoError(t, err) - - // Verify request was forwarded. - mock.mu.Lock() - assert.Equal(t, "my-sandbox", mock.lastEditReq.GetSandbox()) - assert.Equal(t, "chunk-1", mock.lastEditReq.GetChunkId()) - require.NotNil(t, mock.lastEditReq.GetProposedRule()) - assert.Equal(t, "allow-https", mock.lastEditReq.GetProposedRule().GetName()) - require.Len(t, mock.lastEditReq.GetProposedRule().GetEndpoints(), 1) - assert.Equal(t, "example.com", mock.lastEditReq.GetProposedRule().GetEndpoints()[0].GetHost()) - mock.mu.Unlock() -} - -func TestPolicyEditDraftChunk_Error(t *testing.T) { - mock := newMockPolicyServer() - mock.editErr = status.Errorf(codes.InvalidArgument, "invalid rule") - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - err := client.EditDraftChunk(context.Background(), "default", "sb1", "chunk-1", &NetworkPolicyRule{}) - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestPolicyUndoDraftChunk(t *testing.T) { - mock := newMockPolicyServer() - mock.undoResp = &pb.UndoDraftChunkResponse{ - PolicyVersion: 10, - PolicyHash: "sha256:undo", - } - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.UndoDraftChunk(context.Background(), "default", "my-sandbox", "chunk-3") - - require.NoError(t, err) - require.NotNil(t, result) - - // Verify request was forwarded. - mock.mu.Lock() - assert.Equal(t, "my-sandbox", mock.lastUndoReq.GetSandbox()) - assert.Equal(t, "chunk-3", mock.lastUndoReq.GetChunkId()) - mock.mu.Unlock() - - assert.Equal(t, uint32(10), result.PolicyVersion) - assert.Equal(t, "sha256:undo", result.PolicyHash) -} - -func TestPolicyUndoDraftChunk_Error(t *testing.T) { - mock := newMockPolicyServer() - mock.undoErr = status.Errorf(codes.NotFound, "chunk not found") - - client, cleanup := setupPolicyTest(t, mock) - defer cleanup() - - result, err := client.UndoDraftChunk(context.Background(), "default", "sb1", "bad-chunk") - - assert.Nil(t, result) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} diff --git a/sdk/go/openshell/v1/profile.go b/sdk/go/openshell/v1/profile.go deleted file mode 100644 index 7de1951e4f..0000000000 --- a/sdk/go/openshell/v1/profile.go +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// ProviderProfile represents a provider type template. -type ProviderProfile = types.ProviderProfile - -// ProfileCredential defines a single credential required by a provider profile. -type ProfileCredential = types.ProfileCredential - -// ProfileCategory classifies a provider profile. -type ProfileCategory = types.ProfileCategory - -// NetworkEndpoint describes a network endpoint provided by a profile. -type NetworkEndpoint = types.NetworkEndpoint - -// NetworkBinary describes a binary artifact provided by a profile. -type NetworkBinary = types.NetworkBinary - -// ProfileDiscovery holds local discovery configuration for a profile. -type ProfileDiscovery = types.ProfileDiscovery - -// ProfileImportItem is an item submitted for profile import or lint validation. -type ProfileImportItem = types.ProfileImportItem - -// ProfileDiagnostic is a validation finding from Import, Update, or Lint. -type ProfileDiagnostic = types.ProfileDiagnostic - -// ImportResult holds the result of a profile import operation. -type ImportResult = types.ImportResult - -// UpdateResult holds the result of a profile update operation. -type UpdateResult = types.UpdateResult - -// LintResult holds the result of a profile lint operation. -type LintResult = types.LintResult - -// ProfileCategory values. -const ( - ProfileCategoryOther = types.ProfileCategoryOther - ProfileCategoryInference = types.ProfileCategoryInference - ProfileCategoryAgent = types.ProfileCategoryAgent - ProfileCategorySourceControl = types.ProfileCategorySourceControl - ProfileCategoryMessaging = types.ProfileCategoryMessaging - ProfileCategoryData = types.ProfileCategoryData - ProfileCategoryKnowledge = types.ProfileCategoryKnowledge -) - -// ProfileInterface defines operations for managing provider profiles. -type ProfileInterface interface { - List(workspace string, opts ...ListOptions) (*Pager[*ProviderProfile], error) - ListAll(ctx context.Context, workspace string, opts ...ListOptions) ([]*ProviderProfile, error) - Get(ctx context.Context, workspace, id string) (*ProviderProfile, error) - Import(ctx context.Context, workspace string, items []ProfileImportItem) (*ImportResult, error) - Update(ctx context.Context, workspace, id string, expectedResourceVersion uint64, item ProfileImportItem) (*UpdateResult, error) - Lint(ctx context.Context, workspace string, items []ProfileImportItem) (*LintResult, error) - Delete(ctx context.Context, workspace, id string, opts ...DeleteOptions) (*DeletionResult, error) -} diff --git a/sdk/go/openshell/v1/profile_client.go b/sdk/go/openshell/v1/profile_client.go deleted file mode 100644 index 5c3acac9bf..0000000000 --- a/sdk/go/openshell/v1/profile_client.go +++ /dev/null @@ -1,195 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "fmt" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" -) - -type profileClient struct { - client pb.OpenShellClient -} - -func newProfileClient(conn grpc.ClientConnInterface) *profileClient { - return &profileClient{client: pb.NewOpenShellClient(conn)} -} - -func (p *profileClient) List(workspace string, opts ...ListOptions) (*Pager[*ProviderProfile], error) { - pageSize, err := listPageSize(opts) - if err != nil { - return nil, err - } - var pageToken string - if len(opts) > 0 { - pageToken = opts[0].PageToken - } - return newPager(pageToken, func(ctx context.Context, pageToken string) (*Page[*ProviderProfile], error) { - req := &pb.ListProviderProfilesRequest{WorkspaceScope: profileWorkspaceScope(workspace), PageSize: pageSize, PageToken: pageToken} - resp, err := p.client.ListProviderProfiles(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - profiles := make([]*ProviderProfile, 0, len(resp.GetProfiles())) - for _, profile := range resp.GetProfiles() { - profiles = append(profiles, converter.ProviderProfileFromProto(profile)) - } - return &Page[*ProviderProfile]{Items: profiles, NextPageToken: resp.GetNextPageToken()}, nil - }), nil -} - -func (p *profileClient) ListAll(ctx context.Context, workspace string, opts ...ListOptions) ([]*ProviderProfile, error) { - pager, err := p.List(workspace, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -func (p *profileClient) Get(ctx context.Context, workspace, id string) (*ProviderProfile, error) { - resp, err := p.client.GetProviderProfile(ctx, &pb.GetProviderProfileRequest{ - Id: id, - WorkspaceScope: profileWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.ProviderProfileFromProto(resp.GetProfile()), nil -} - -func (p *profileClient) Import(ctx context.Context, workspace string, items []ProfileImportItem) (*ImportResult, error) { - pbItems := make([]*pb.ProviderProfileImportItem, len(items)) - for i := range items { - pbItems[i] = converter.ProfileImportItemToProto(&items[i]) - } - if profilesRequireStablePlaceholders(pbItems) { - if _, err := p.lint(ctx, workspace, pbItems); err != nil { - return nil, err - } - } - - resp, err := p.client.ImportProviderProfiles(ctx, &pb.ImportProviderProfilesRequest{ - Profiles: pbItems, - WorkspaceScope: profileWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - - result := &ImportResult{ - Imported: resp.GetImported(), - } - - for _, d := range resp.GetDiagnostics() { - if diag := converter.ProfileDiagnosticFromProto(d); diag != nil { - result.Diagnostics = append(result.Diagnostics, *diag) - } - } - - for _, pp := range resp.GetProfiles() { - if profile := converter.ProviderProfileFromProto(pp); profile != nil { - result.Profiles = append(result.Profiles, *profile) - } - } - - return result, nil -} - -func (p *profileClient) Update(ctx context.Context, workspace, id string, expectedResourceVersion uint64, item ProfileImportItem) (*UpdateResult, error) { - pbItem := converter.ProfileImportItemToProto(&item) - if items := []*pb.ProviderProfileImportItem{pbItem}; profilesRequireStablePlaceholders(items) { - if _, err := p.lint(ctx, workspace, items); err != nil { - return nil, err - } - } - resp, err := p.client.UpdateProviderProfiles(ctx, &pb.UpdateProviderProfilesRequest{ - Id: id, - Profile: pbItem, - ExpectedResourceVersion: expectedResourceVersion, - WorkspaceScope: profileWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - - result := &UpdateResult{ - Updated: resp.GetUpdated(), - Profile: converter.ProviderProfileFromProto(resp.GetProfile()), - } - - for _, d := range resp.GetDiagnostics() { - if diag := converter.ProfileDiagnosticFromProto(d); diag != nil { - result.Diagnostics = append(result.Diagnostics, *diag) - } - } - - return result, nil -} - -func (p *profileClient) Lint(ctx context.Context, workspace string, items []ProfileImportItem) (*LintResult, error) { - pbItems := make([]*pb.ProviderProfileImportItem, len(items)) - for i := range items { - pbItems[i] = converter.ProfileImportItemToProto(&items[i]) - } - - resp, err := p.lint(ctx, workspace, pbItems) - if err != nil { - return nil, err - } - - result := &LintResult{ - Valid: resp.GetValid(), - } - - for _, d := range resp.GetDiagnostics() { - if diag := converter.ProfileDiagnosticFromProto(d); diag != nil { - result.Diagnostics = append(result.Diagnostics, *diag) - } - } - - return result, nil -} - -func profilesRequireStablePlaceholders(items []*pb.ProviderProfileImportItem) bool { - for _, item := range items { - for _, credential := range item.GetProfile().GetCredentials() { - if credential.GetStablePlaceholder() { - return true - } - } - } - return false -} - -// Lint acknowledges feature support before an opt-in is persisted. Legacy -// gateways can otherwise silently discard unknown credential fields. -func (p *profileClient) lint(ctx context.Context, workspace string, items []*pb.ProviderProfileImportItem) (*pb.LintProviderProfilesResponse, error) { - resp, err := p.client.LintProviderProfiles(ctx, &pb.LintProviderProfilesRequest{ - Profiles: items, - WorkspaceScope: profileWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - if profilesRequireStablePlaceholders(items) && !resp.GetSupportsStablePlaceholder() { - return nil, fmt.Errorf("gateway does not support stable_placeholder; upgrade the gateway before enabling it") - } - return resp, nil -} - -func (p *profileClient) Delete(ctx context.Context, workspace, id string, opts ...DeleteOptions) (*DeletionResult, error) { - resp, err := p.client.DeleteProviderProfile(ctx, &pb.DeleteProviderProfileRequest{ - AllowMissing: allowMissing(opts), - Id: id, - WorkspaceScope: profileWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return &DeletionResult{Outcome: DeletionOutcome(resp.GetOutcome())}, nil -} diff --git a/sdk/go/openshell/v1/profile_client_test.go b/sdk/go/openshell/v1/profile_client_test.go deleted file mode 100644 index f06ebe28a2..0000000000 --- a/sdk/go/openshell/v1/profile_client_test.go +++ /dev/null @@ -1,615 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "fmt" - "net" - "sync" - "testing" - - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - sbv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" -) - -// --- Mock server for provider profiles --- - -type mockProfileServer struct { - pb.UnimplementedOpenShellServer - mu sync.Mutex - profiles map[string]*pb.ProviderProfile // key: profile ID - - listErr error - getErr error - importErr error - updateErr error - lintErr error - deleteErr error - - lastListReq *pb.ListProviderProfilesRequest - supportsStablePlaceholder bool -} - -func newMockProfileServer() *mockProfileServer { - return &mockProfileServer{ - profiles: make(map[string]*pb.ProviderProfile), - } -} - -func (s *mockProfileServer) ListProviderProfiles(_ context.Context, req *pb.ListProviderProfilesRequest) (*pb.ListProviderProfilesResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastListReq = req - if s.listErr != nil { - return nil, s.listErr - } - - var profiles []*pb.ProviderProfile - for _, p := range s.profiles { - profiles = append(profiles, p) - } - return &pb.ListProviderProfilesResponse{Profiles: profiles}, nil -} - -func (s *mockProfileServer) GetProviderProfile(_ context.Context, req *pb.GetProviderProfileRequest) (*pb.ProviderProfileResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.getErr != nil { - return nil, s.getErr - } - - p, ok := s.profiles[req.GetId()] - if !ok { - return nil, status.Errorf(codes.NotFound, "profile %q not found", req.GetId()) - } - return &pb.ProviderProfileResponse{Profile: p}, nil -} - -func (s *mockProfileServer) ImportProviderProfiles(_ context.Context, req *pb.ImportProviderProfilesRequest) (*pb.ImportProviderProfilesResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.importErr != nil { - return nil, s.importErr - } - - var imported []*pb.ProviderProfile - for _, item := range req.GetProfiles() { - p := item.GetProfile() - if p != nil { - s.profiles[p.GetId()] = p - imported = append(imported, p) - } - } - return &pb.ImportProviderProfilesResponse{ - Profiles: imported, - Imported: len(imported) > 0, - }, nil -} - -func (s *mockProfileServer) UpdateProviderProfiles(_ context.Context, req *pb.UpdateProviderProfilesRequest) (*pb.UpdateProviderProfilesResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.updateErr != nil { - return nil, s.updateErr - } - - id := req.GetId() - existing, ok := s.profiles[id] - if !ok { - return nil, status.Errorf(codes.NotFound, "profile %q not found", id) - } - - if req.GetExpectedResourceVersion() != existing.GetResourceVersion() { - return nil, status.Errorf(codes.FailedPrecondition, "resource version mismatch") - } - - p := req.GetProfile().GetProfile() - if p != nil { - p.ResourceVersion = existing.GetResourceVersion() + 1 - s.profiles[id] = p - } - - return &pb.UpdateProviderProfilesResponse{ - Profile: p, - Updated: true, - }, nil -} - -func (s *mockProfileServer) LintProviderProfiles(_ context.Context, req *pb.LintProviderProfilesRequest) (*pb.LintProviderProfilesResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.lintErr != nil { - return nil, s.lintErr - } - - // Simple lint: valid if all profiles have an ID - var diagnostics []*pb.ProviderProfileDiagnostic - valid := true - for _, item := range req.GetProfiles() { - p := item.GetProfile() - if p != nil && p.GetId() == "" { - valid = false - diagnostics = append(diagnostics, &pb.ProviderProfileDiagnostic{ - Source: item.GetSource(), - Field: "id", - Message: "profile ID is required", - Severity: "error", - }) - } - } - return &pb.LintProviderProfilesResponse{ - Diagnostics: diagnostics, - Valid: valid, - SupportsStablePlaceholder: s.supportsStablePlaceholder, - }, nil -} - -func (s *mockProfileServer) DeleteProviderProfile(_ context.Context, req *pb.DeleteProviderProfileRequest) (*pb.DeleteProviderProfileResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.deleteErr != nil { - return nil, s.deleteErr - } - - _, ok := s.profiles[req.GetId()] - if !ok { - return nil, status.Errorf(codes.NotFound, "profile %q not found", req.GetId()) - } - delete(s.profiles, req.GetId()) - return &pb.DeleteProviderProfileResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_COMPLETED}, nil -} - -// --- Test setup --- - -func setupProfileTest(t *testing.T, mock *mockProfileServer) (*profileClient, func()) { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - - return newProfileClient(conn), func() { - _ = conn.Close() - srv.Stop() - } -} - -// seedProfile adds a profile to the mock server store for testing. -func seedProfile(mock *mockProfileServer, id, displayName string, category pb.ProviderProfileCategory) { - mock.mu.Lock() - defer mock.mu.Unlock() - mock.profiles[id] = &pb.ProviderProfile{ - Id: id, - DisplayName: displayName, - Description: "Test profile " + id, - Category: category, - ResourceVersion: 1, - Credentials: []*pb.ProviderProfileCredential{ - {Name: "api-key", Description: "API Key", Required: true, Refresh: &pb.ProviderCredentialRefresh{}}, - }, - Endpoints: []*sbv1.NetworkEndpoint{ - {Host: "localhost", Port: 8080, Protocol: "http"}, - }, - Binaries: []*sbv1.NetworkBinary{ - {Path: "/usr/bin/provider"}, - }, - } -} - -// --- List tests --- - -func TestProfileList(t *testing.T) { - mock := newMockProfileServer() - seedProfile(mock, "p1", "Profile One", pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE) - seedProfile(mock, "p2", "Profile Two", pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_AGENT) - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - profiles, err := client.ListAll(context.Background(), "default") - - require.NoError(t, err) - assert.Len(t, profiles, 2) -} - -func TestProfileList_Empty(t *testing.T) { - mock := newMockProfileServer() - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - profiles, err := client.ListAll(context.Background(), "default") - - require.NoError(t, err) - assert.NotNil(t, profiles) - assert.Empty(t, profiles) -} - -func TestProfileList_WithOptions(t *testing.T) { - mock := newMockProfileServer() - seedProfile(mock, "p1", "Profile One", pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE) - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - profiles, err := client.ListAll(context.Background(), "default", ListOptions{PageSize: 10}) - - require.NoError(t, err) - assert.Len(t, profiles, 1) - require.NotNil(t, mock.lastListReq) - assert.Equal(t, int32(10), mock.lastListReq.GetPageSize()) - assert.Empty(t, mock.lastListReq.GetPageToken()) -} - -func TestProfileList_Error(t *testing.T) { - mock := newMockProfileServer() - mock.listErr = status.Errorf(codes.Unavailable, "unavailable") - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - profiles, err := client.ListAll(context.Background(), "default") - - assert.Nil(t, profiles) - require.Error(t, err) - assert.True(t, IsUnavailable(err)) -} - -// --- Get tests --- - -func TestProfileGet(t *testing.T) { - mock := newMockProfileServer() - seedProfile(mock, "p1", "Profile One", pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE) - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - profile, err := client.Get(context.Background(), "default", "p1") - - require.NoError(t, err) - require.NotNil(t, profile) - assert.Equal(t, "p1", profile.ID) - assert.Equal(t, "Profile One", profile.DisplayName) - assert.Equal(t, ProfileCategoryInference, profile.Category) - assert.Equal(t, uint64(1), profile.ResourceVersion) - // Verify credential deep copy - require.Len(t, profile.Credentials, 1) - assert.Equal(t, "api-key", profile.Credentials[0].Name) - assert.True(t, profile.Credentials[0].Required) - assert.True(t, profile.Credentials[0].Secret) // derived from Refresh != nil - // Verify endpoint deep copy - require.Len(t, profile.Endpoints, 1) - assert.Equal(t, "localhost", profile.Endpoints[0].Host) - assert.Equal(t, uint32(8080), profile.Endpoints[0].Port) - // Verify binary deep copy - require.Len(t, profile.Binaries, 1) - assert.Equal(t, "/usr/bin/provider", profile.Binaries[0].Path) -} - -func TestProfileGet_NotFound(t *testing.T) { - mock := newMockProfileServer() - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - profile, err := client.Get(context.Background(), "default", "nonexistent") - - assert.Nil(t, profile) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestProfileGet_Error(t *testing.T) { - mock := newMockProfileServer() - mock.getErr = status.Errorf(codes.Internal, "internal error") - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - profile, err := client.Get(context.Background(), "default", "p1") - - assert.Nil(t, profile) - require.Error(t, err) -} - -// --- Import tests --- - -func TestProfileStablePlaceholderCompatibility(t *testing.T) { - for _, supports := range []bool{false, true} { - for _, enabled := range []bool{false, true} { - t.Run(fmt.Sprintf("gateway_supports=%t/enabled=%t", supports, enabled), func(t *testing.T) { - mock := newMockProfileServer() - mock.supportsStablePlaceholder = supports - if !enabled { - // Ordinary writes must not acquire a new dependency on lint. - mock.lintErr = status.Error(codes.Unimplemented, "legacy lint unavailable") - } - seedProfile(mock, "existing", "Existing", pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE) - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - item := ProfileImportItem{Profile: ProviderProfile{ - ID: "new", - Credentials: []ProfileCredential{{Name: "token", StablePlaceholder: enabled}}, - }} - imported, importErr := client.Import(context.Background(), "default", []ProfileImportItem{item}) - item.Profile.ID = "existing" - updated, updateErr := client.Update(context.Background(), "default", "existing", 1, item) - if enabled && !supports { - require.ErrorContains(t, importErr, "does not support stable_placeholder") - require.ErrorContains(t, updateErr, "does not support stable_placeholder") - mock.mu.Lock() - defer mock.mu.Unlock() - assert.NotContains(t, mock.profiles, "new") - assert.Equal(t, uint64(1), mock.profiles["existing"].GetResourceVersion()) - assert.False(t, mock.profiles["existing"].GetCredentials()[0].GetStablePlaceholder()) - } else { - require.NoError(t, importErr) - require.NoError(t, updateErr) - assert.True(t, imported.Imported) - assert.True(t, updated.Updated) - assert.Equal(t, enabled, imported.Profiles[0].Credentials[0].StablePlaceholder) - assert.Equal(t, enabled, updated.Profile.Credentials[0].StablePlaceholder) - } - }) - } - } -} - -func TestProfileImport(t *testing.T) { - mock := newMockProfileServer() - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - items := []ProfileImportItem{ - { - Profile: ProviderProfile{ - ID: "p1", - DisplayName: "New Profile", - Category: ProfileCategoryInference, - }, - Source: "test.yaml", - }, - } - - result, err := client.Import(context.Background(), "default", items) - - require.NoError(t, err) - require.NotNil(t, result) - assert.True(t, result.Imported) - assert.Len(t, result.Profiles, 1) - assert.Equal(t, "p1", result.Profiles[0].ID) -} - -func TestProfileImport_MultipleItems(t *testing.T) { - mock := newMockProfileServer() - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - items := []ProfileImportItem{ - { - Profile: ProviderProfile{ID: "p1", DisplayName: "Profile 1"}, - Source: "a.yaml", - }, - { - Profile: ProviderProfile{ID: "p2", DisplayName: "Profile 2"}, - Source: "b.yaml", - }, - } - - result, err := client.Import(context.Background(), "default", items) - - require.NoError(t, err) - require.NotNil(t, result) - assert.True(t, result.Imported) - assert.Len(t, result.Profiles, 2) -} - -func TestProfileImport_Error(t *testing.T) { - mock := newMockProfileServer() - mock.importErr = status.Errorf(codes.InvalidArgument, "bad request") - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - items := []ProfileImportItem{ - {Profile: ProviderProfile{ID: "p1"}, Source: "test.yaml"}, - } - - result, err := client.Import(context.Background(), "default", items) - - assert.Nil(t, result) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -// --- Update tests --- - -func TestProfileUpdate(t *testing.T) { - mock := newMockProfileServer() - seedProfile(mock, "p1", "Original", pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE) - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - item := ProfileImportItem{ - Profile: ProviderProfile{ - ID: "p1", - DisplayName: "Updated", - Category: ProfileCategoryAgent, - }, - Source: "update.yaml", - } - - result, err := client.Update(context.Background(), "default", "p1", 1, item) - - require.NoError(t, err) - require.NotNil(t, result) - assert.True(t, result.Updated) - require.NotNil(t, result.Profile) - assert.Equal(t, uint64(2), result.Profile.ResourceVersion) // bumped by mock -} - -func TestProfileUpdate_NotFound(t *testing.T) { - mock := newMockProfileServer() - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - item := ProfileImportItem{ - Profile: ProviderProfile{ID: "missing"}, - Source: "test.yaml", - } - - result, err := client.Update(context.Background(), "default", "missing", 1, item) - - assert.Nil(t, result) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestProfileUpdate_VersionMismatch(t *testing.T) { - mock := newMockProfileServer() - seedProfile(mock, "p1", "Original", pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE) - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - item := ProfileImportItem{ - Profile: ProviderProfile{ID: "p1"}, - Source: "test.yaml", - } - - // Use wrong version (99 instead of 1) - result, err := client.Update(context.Background(), "default", "p1", 99, item) - - assert.Nil(t, result) - require.Error(t, err) -} - -func TestProfileUpdate_Error(t *testing.T) { - mock := newMockProfileServer() - mock.updateErr = status.Errorf(codes.Internal, "internal error") - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - item := ProfileImportItem{ - Profile: ProviderProfile{ID: "p1"}, - Source: "test.yaml", - } - - result, err := client.Update(context.Background(), "default", "p1", 1, item) - - assert.Nil(t, result) - require.Error(t, err) -} - -// --- Lint tests --- - -func TestProfileLint_Valid(t *testing.T) { - mock := newMockProfileServer() - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - items := []ProfileImportItem{ - { - Profile: ProviderProfile{ID: "p1", DisplayName: "Good Profile"}, - Source: "test.yaml", - }, - } - - result, err := client.Lint(context.Background(), "default", items) - - require.NoError(t, err) - require.NotNil(t, result) - assert.True(t, result.Valid) - assert.Empty(t, result.Diagnostics) -} - -func TestProfileLint_Invalid(t *testing.T) { - mock := newMockProfileServer() - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - items := []ProfileImportItem{ - { - Profile: ProviderProfile{ID: "", DisplayName: "Bad Profile"}, // empty ID triggers lint error - Source: "bad.yaml", - }, - } - - result, err := client.Lint(context.Background(), "default", items) - - require.NoError(t, err) - require.NotNil(t, result) - assert.False(t, result.Valid) - require.Len(t, result.Diagnostics, 1) - assert.Equal(t, "id", result.Diagnostics[0].Field) - assert.Equal(t, "error", result.Diagnostics[0].Severity) -} - -func TestProfileLint_Error(t *testing.T) { - mock := newMockProfileServer() - mock.lintErr = status.Errorf(codes.Unavailable, "unavailable") - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - items := []ProfileImportItem{ - {Profile: ProviderProfile{ID: "p1"}, Source: "test.yaml"}, - } - - result, err := client.Lint(context.Background(), "default", items) - - assert.Nil(t, result) - require.Error(t, err) - assert.True(t, IsUnavailable(err)) -} - -// --- Delete tests --- - -func TestProfileDelete(t *testing.T) { - mock := newMockProfileServer() - seedProfile(mock, "p1", "Profile One", pb.ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE) - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - deleted, err := client.Delete(context.Background(), "default", "p1") - - require.NoError(t, err) - assert.Equal(t, DeletionCompleted, deleted.Outcome) - - // Verify subsequent Get returns NotFound - profile, err := client.Get(context.Background(), "default", "p1") - assert.Nil(t, profile) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestProfileDelete_NotFound(t *testing.T) { - mock := newMockProfileServer() - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - deleted, err := client.Delete(context.Background(), "default", "nonexistent") - - assert.Nil(t, deleted) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestProfileDelete_Error(t *testing.T) { - mock := newMockProfileServer() - mock.deleteErr = status.Errorf(codes.Internal, "internal error") - client, cleanup := setupProfileTest(t, mock) - defer cleanup() - - deleted, err := client.Delete(context.Background(), "default", "p1") - - assert.Nil(t, deleted) - require.Error(t, err) -} diff --git a/sdk/go/openshell/v1/provider.go b/sdk/go/openshell/v1/provider.go deleted file mode 100644 index b62960dac1..0000000000 --- a/sdk/go/openshell/v1/provider.go +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// Provider represents an AI provider registration. -type Provider = types.Provider - -// ProviderSpec holds provider-specific configuration and credentials. -type ProviderSpec = types.ProviderSpec - -// ProviderInterface defines CRUD and Ensure operations on providers, -// plus sub-client accessors for profiles and credential refresh. -type ProviderInterface interface { - Create(ctx context.Context, workspace string, provider *Provider) (*Provider, error) - Get(ctx context.Context, workspace, name string) (*Provider, error) - List(workspace string, opts ...ListOptions) (*Pager[*Provider], error) - ListAll(ctx context.Context, workspace string, opts ...ListOptions) ([]*Provider, error) - Update(ctx context.Context, workspace string, provider *Provider) (*Provider, error) - Delete(ctx context.Context, workspace, name string, opts ...DeleteOptions) (*DeletionResult, error) - Ensure(ctx context.Context, workspace string, provider *Provider) (*Provider, error) - Profiles() ProfileInterface - Refresh() RefreshInterface -} diff --git a/sdk/go/openshell/v1/provider_client.go b/sdk/go/openshell/v1/provider_client.go deleted file mode 100644 index 08f75ead4c..0000000000 --- a/sdk/go/openshell/v1/provider_client.go +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "sort" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" -) - -type providerClient struct { - client pb.OpenShellClient - profiles *profileClient - refresh *refreshClient -} - -func newProviderClient(conn grpc.ClientConnInterface) *providerClient { - return &providerClient{ - client: pb.NewOpenShellClient(conn), - profiles: newProfileClient(conn), - refresh: newRefreshClient(conn), - } -} - -func (p *providerClient) Profiles() ProfileInterface { - return p.profiles -} - -func (p *providerClient) Refresh() RefreshInterface { - return p.refresh -} - -func (p *providerClient) Create(ctx context.Context, workspace string, provider *Provider) (*Provider, error) { - resp, err := p.client.CreateProvider(ctx, &pb.CreateProviderRequest{ - Provider: converter.ProviderToProto(provider), - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.ProviderFromProto(resp.GetProvider()), nil -} - -func (p *providerClient) Get(ctx context.Context, workspace, name string) (*Provider, error) { - resp, err := p.client.GetProvider(ctx, &pb.GetProviderRequest{ - Name: name, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.ProviderFromProto(resp.GetProvider()), nil -} - -func (p *providerClient) List(workspace string, opts ...ListOptions) (*Pager[*Provider], error) { - pageSize, err := listPageSize(opts) - if err != nil { - return nil, err - } - var pageToken string - var allWorkspaces bool - if len(opts) > 0 { - pageToken = opts[0].PageToken - allWorkspaces = opts[0].AllWorkspaces - } - workspaceScope := namedWorkspaceScope(workspace) - if allWorkspaces { - workspaceScope = allWorkspacesScope() - } - return newPager(pageToken, func(ctx context.Context, pageToken string) (*Page[*Provider], error) { - req := &pb.ListProvidersRequest{WorkspaceScope: workspaceScope, PageSize: pageSize, PageToken: pageToken} - resp, err := p.client.ListProviders(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - providers := make([]*Provider, 0, len(resp.GetProviders())) - for _, proto := range resp.GetProviders() { - providers = append(providers, converter.ProviderFromProto(proto)) - } - return &Page[*Provider]{Items: providers, NextPageToken: resp.GetNextPageToken()}, nil - }), nil -} - -func (p *providerClient) ListAll(ctx context.Context, workspace string, opts ...ListOptions) ([]*Provider, error) { - pager, err := p.List(workspace, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -func (p *providerClient) Update(ctx context.Context, workspace string, provider *Provider) (*Provider, error) { - proto := converter.ProviderToProto(provider) - req := &pb.UpdateProviderRequest{ - Provider: proto, - WorkspaceScope: namedWorkspaceScope(workspace), - } - if proto != nil { - req.CredentialExpirationTimes = proto.CredentialExpirationTimes - for key, expiresAt := range provider.Spec.CredentialExpiresAt { - if expiresAt.IsZero() { - req.ClearCredentialExpirationKeys = append(req.ClearCredentialExpirationKeys, key) - } - } - sort.Strings(req.ClearCredentialExpirationKeys) - } - - resp, err := p.client.UpdateProvider(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.ProviderFromProto(resp.GetProvider()), nil -} - -func (p *providerClient) Delete(ctx context.Context, workspace, name string, opts ...DeleteOptions) (*DeletionResult, error) { - resp, err := p.client.DeleteProvider(ctx, &pb.DeleteProviderRequest{ - AllowMissing: allowMissing(opts), - Name: name, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return &DeletionResult{Outcome: DeletionOutcome(resp.GetOutcome())}, nil -} - -func (p *providerClient) Ensure(ctx context.Context, workspace string, provider *Provider) (*Provider, error) { - if provider == nil { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "provider must not be nil"} - } - existing, err := p.Get(ctx, workspace, provider.Name) - if err != nil { - if !IsNotFound(err) { - return nil, err - } - return p.Create(ctx, workspace, provider) - } - - updated := *provider - updated.ID = existing.ID - updated.ResourceVersion = existing.ResourceVersion - return p.Update(ctx, workspace, &updated) -} diff --git a/sdk/go/openshell/v1/provider_client_test.go b/sdk/go/openshell/v1/provider_client_test.go deleted file mode 100644 index 7ce76f8791..0000000000 --- a/sdk/go/openshell/v1/provider_client_test.go +++ /dev/null @@ -1,356 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "net" - "testing" - "time" - - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" -) - -type mockProviderServer struct { - pb.UnimplementedOpenShellServer - providers map[string]*dm.Provider - lastList *pb.ListProvidersRequest - lastUpdate *pb.UpdateProviderRequest - createErr error - getErr error - listErr error - updateErr error - deleteErr error -} - -func newMockProviderServer() *mockProviderServer { - return &mockProviderServer{ - providers: make(map[string]*dm.Provider), - } -} - -func (s *mockProviderServer) CreateProvider(_ context.Context, req *pb.CreateProviderRequest) (*pb.ProviderResponse, error) { - if s.createErr != nil { - return nil, s.createErr - } - p := req.GetProvider() - if p.GetMetadata() != nil { - s.providers[p.GetMetadata().GetName()] = p - } - return &pb.ProviderResponse{Provider: p}, nil -} - -func (s *mockProviderServer) GetProvider(_ context.Context, req *pb.GetProviderRequest) (*pb.ProviderResponse, error) { - if s.getErr != nil { - return nil, s.getErr - } - p, ok := s.providers[req.GetName()] - if !ok { - return nil, status.Errorf(codes.NotFound, "provider %q not found", req.GetName()) - } - return &pb.ProviderResponse{Provider: p}, nil -} - -func (s *mockProviderServer) ListProviders(_ context.Context, req *pb.ListProvidersRequest) (*pb.ListProvidersResponse, error) { - s.lastList = req - if s.listErr != nil { - return nil, s.listErr - } - var list []*dm.Provider - for _, p := range s.providers { - list = append(list, p) - } - return &pb.ListProvidersResponse{Providers: list}, nil -} - -func (s *mockProviderServer) UpdateProvider(_ context.Context, req *pb.UpdateProviderRequest) (*pb.ProviderResponse, error) { - s.lastUpdate = req - if s.updateErr != nil { - return nil, s.updateErr - } - p := req.GetProvider() - if p.GetMetadata() != nil { - name := p.GetMetadata().GetName() - if _, ok := s.providers[name]; !ok { - return nil, status.Errorf(codes.NotFound, "provider %q not found", name) - } - s.providers[name] = p - } - return &pb.ProviderResponse{Provider: p}, nil -} - -func (s *mockProviderServer) DeleteProvider(_ context.Context, req *pb.DeleteProviderRequest) (*pb.DeleteProviderResponse, error) { - if s.deleteErr != nil { - return nil, s.deleteErr - } - delete(s.providers, req.GetName()) - return &pb.DeleteProviderResponse{}, nil -} - -func setupProviderTest(t *testing.T, mock *mockProviderServer) (*providerClient, func()) { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - - return newProviderClient(conn), func() { - _ = conn.Close() - srv.Stop() - } -} - -func TestProviderCreate(t *testing.T) { - mock := newMockProviderServer() - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - p := &Provider{ - Name: "my-claude", - Type: "claude", - Spec: ProviderSpec{ - Credentials: map[string]string{"API_KEY": "secret"}, - Config: map[string]string{"region": "us-east-1"}, - }, - } - - result, err := client.Create(context.Background(), "default", p) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, "my-claude", result.Name) - assert.Equal(t, "claude", result.Type) - assert.Nil(t, result.Spec.Credentials, "credentials are write-only and should not be returned") -} - -func TestProviderCreate_AlreadyExists(t *testing.T) { - mock := newMockProviderServer() - mock.createErr = status.Error(codes.AlreadyExists, "provider already exists") - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - _, err := client.Create(context.Background(), "default", &Provider{Name: "dup"}) - - require.Error(t, err) - assert.True(t, IsAlreadyExists(err)) -} - -func TestProviderGet(t *testing.T) { - mock := newMockProviderServer() - mock.providers["existing"] = &dm.Provider{ - Metadata: &dm.ObjectMeta{Id: "p1", Name: "existing"}, - Type: "gitlab", - } - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - result, err := client.Get(context.Background(), "default", "existing") - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, "existing", result.Name) - assert.Equal(t, "gitlab", result.Type) -} - -func TestProviderGet_NotFound(t *testing.T) { - mock := newMockProviderServer() - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - _, err := client.Get(context.Background(), "default", "nonexistent") - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestProviderList(t *testing.T) { - mock := newMockProviderServer() - mock.providers["p1"] = &dm.Provider{ - Metadata: &dm.ObjectMeta{Name: "p1"}, - Type: "claude", - } - mock.providers["p2"] = &dm.Provider{ - Metadata: &dm.ObjectMeta{Name: "p2"}, - Type: "gitlab", - } - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - result, err := client.ListAll(context.Background(), "default") - - require.NoError(t, err) - assert.Len(t, result, 2) -} - -func TestProviderList_Empty(t *testing.T) { - mock := newMockProviderServer() - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - result, err := client.ListAll(context.Background(), "default") - - require.NoError(t, err) - assert.NotNil(t, result) - assert.Empty(t, result) -} - -func TestProviderListAll_SelectsAllWorkspaces(t *testing.T) { - mock := newMockProviderServer() - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - providers, err := client.ListAll(context.Background(), "", ListOptions{ - PageSize: 10, - AllWorkspaces: true, - }) - - require.NoError(t, err) - assert.Empty(t, providers) - require.NotNil(t, mock.lastList) - assert.Equal(t, int32(10), mock.lastList.GetPageSize()) - assert.NotNil(t, mock.lastList.GetWorkspaceScope().GetAllWorkspaces()) -} - -func TestProviderUpdate(t *testing.T) { - mock := newMockProviderServer() - mock.providers["updatable"] = &dm.Provider{ - Metadata: &dm.ObjectMeta{Name: "updatable"}, - Type: "claude", - } - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - p := &Provider{ - Name: "updatable", - Type: "claude", - Spec: ProviderSpec{ - Credentials: map[string]string{"API_KEY": "new-secret"}, - }, - } - - result, err := client.Update(context.Background(), "default", p) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, "updatable", result.Name) -} - -func TestProviderUpdate_ClearsZeroCredentialExpiry(t *testing.T) { - mock := newMockProviderServer() - mock.providers["updatable"] = &dm.Provider{Metadata: &dm.ObjectMeta{Name: "updatable"}} - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - _, err := client.Update(context.Background(), "default", &Provider{ - Name: "updatable", - Spec: ProviderSpec{CredentialExpiresAt: map[string]time.Time{ - "clear_me": {}, - "keep_me": time.Unix(1_700_000_000, 0), - }}, - }) - - require.NoError(t, err) - require.NotNil(t, mock.lastUpdate) - assert.Equal(t, []string{"clear_me"}, mock.lastUpdate.GetClearCredentialExpirationKeys()) - assert.Contains(t, mock.lastUpdate.GetCredentialExpirationTimes(), "keep_me") - assert.NotContains(t, mock.lastUpdate.GetCredentialExpirationTimes(), "clear_me") -} - -func TestProviderUpdate_NotFound(t *testing.T) { - mock := newMockProviderServer() - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - _, err := client.Update(context.Background(), "default", &Provider{Name: "missing"}) - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestProviderDelete(t *testing.T) { - mock := newMockProviderServer() - mock.providers["deleteme"] = &dm.Provider{ - Metadata: &dm.ObjectMeta{Name: "deleteme"}, - } - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - _, err := client.Delete(context.Background(), "default", "deleteme") - - require.NoError(t, err) - assert.Empty(t, mock.providers["deleteme"]) -} - -func TestProviderDelete_NotFound(t *testing.T) { - mock := newMockProviderServer() - mock.deleteErr = status.Error(codes.NotFound, "not found") - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - _, err := client.Delete(context.Background(), "default", "nonexistent") - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestProviderEnsure_Creates(t *testing.T) { - mock := newMockProviderServer() - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - p := &Provider{ - Name: "new-provider", - Type: "claude", - Spec: ProviderSpec{ - Credentials: map[string]string{"KEY": "val"}, - }, - } - - result, err := client.Ensure(context.Background(), "default", p) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, "new-provider", result.Name) -} - -func TestProviderEnsure_Updates(t *testing.T) { - mock := newMockProviderServer() - mock.providers["existing"] = &dm.Provider{ - Metadata: &dm.ObjectMeta{Name: "existing"}, - Type: "claude", - Config: map[string]string{"old": "config"}, - } - client, cleanup := setupProviderTest(t, mock) - defer cleanup() - - p := &Provider{ - Name: "existing", - Type: "claude", - Spec: ProviderSpec{ - Config: map[string]string{"new": "config"}, - }, - } - - result, err := client.Ensure(context.Background(), "default", p) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, "existing", result.Name) -} diff --git a/sdk/go/openshell/v1/refresh.go b/sdk/go/openshell/v1/refresh.go deleted file mode 100644 index 2a8a949752..0000000000 --- a/sdk/go/openshell/v1/refresh.go +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// RefreshStrategy describes how credentials are refreshed. -type RefreshStrategy = types.RefreshStrategy - -// RefreshStatus reports the current state of credential refresh for a provider credential. -type RefreshStatus = types.RefreshStatus - -// RefreshConfig holds configuration parameters for credential refresh. -type RefreshConfig = types.RefreshConfig - -// RefreshStrategy values. -const ( - RefreshStrategyStatic = types.RefreshStrategyStatic - RefreshStrategyExternal = types.RefreshStrategyExternal - RefreshStrategyOAuth2RefreshToken = types.RefreshStrategyOAuth2RefreshToken - RefreshStrategyOAuth2ClientCredentials = types.RefreshStrategyOAuth2ClientCredentials - RefreshStrategyGoogleServiceAccountJWT = types.RefreshStrategyGoogleServiceAccountJWT -) - -// RefreshRecoveryAction describes the action required after a credential failure. -type RefreshRecoveryAction = types.RefreshRecoveryAction - -// RefreshRecoveryAction values. -const ( - RefreshRecoveryActionUnspecified = types.RefreshRecoveryActionUnspecified - RefreshRecoveryActionRetry = types.RefreshRecoveryActionRetry - RefreshRecoveryActionReauthorize = types.RefreshRecoveryActionReauthorize - RefreshRecoveryActionFixConfiguration = types.RefreshRecoveryActionFixConfiguration - RefreshRecoveryActionInvestigate = types.RefreshRecoveryActionInvestigate -) - -// RefreshInterface defines operations for managing provider credential refresh. -type RefreshInterface interface { - GetStatus(ctx context.Context, workspace, provider, credentialKey string) ([]*RefreshStatus, error) - Configure(ctx context.Context, workspace string, config *RefreshConfig) (*RefreshStatus, error) - Rotate(ctx context.Context, workspace, provider, credentialKey string) (*RefreshStatus, error) - Delete(ctx context.Context, workspace, provider, credentialKey string, opts ...DeleteOptions) (*DeletionResult, error) -} diff --git a/sdk/go/openshell/v1/refresh_client.go b/sdk/go/openshell/v1/refresh_client.go deleted file mode 100644 index 5038403871..0000000000 --- a/sdk/go/openshell/v1/refresh_client.go +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" -) - -type refreshClient struct { - client pb.OpenShellClient -} - -func newRefreshClient(conn grpc.ClientConnInterface) *refreshClient { - return &refreshClient{client: pb.NewOpenShellClient(conn)} -} - -func (r *refreshClient) GetStatus(ctx context.Context, workspace, provider, credentialKey string) ([]*RefreshStatus, error) { - resp, err := r.client.GetProviderRefreshStatus(ctx, &pb.GetProviderRefreshStatusRequest{ - Provider: provider, - CredentialKey: credentialKey, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - - statuses := make([]*RefreshStatus, 0, len(resp.GetCredentials())) - for _, s := range resp.GetCredentials() { - statuses = append(statuses, converter.RefreshStatusFromProto(s)) - } - return statuses, nil -} - -func (r *refreshClient) Configure(ctx context.Context, workspace string, config *RefreshConfig) (*RefreshStatus, error) { - req := converter.RefreshConfigToProto(config) - req.WorkspaceScope = namedWorkspaceScope(workspace) - resp, err := r.client.ConfigureProviderRefresh(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.RefreshStatusFromProto(resp.GetStatus()), nil -} - -func (r *refreshClient) Rotate(ctx context.Context, workspace, provider, credentialKey string) (*RefreshStatus, error) { - resp, err := r.client.RotateProviderCredential(ctx, &pb.RotateProviderCredentialRequest{ - Provider: provider, - CredentialKey: credentialKey, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.RefreshStatusFromProto(resp.GetStatus()), nil -} - -func (r *refreshClient) Delete(ctx context.Context, workspace, provider, credentialKey string, opts ...DeleteOptions) (*DeletionResult, error) { - resp, err := r.client.DeleteProviderRefresh(ctx, &pb.DeleteProviderRefreshRequest{ - AllowMissing: allowMissing(opts), - Provider: provider, - CredentialKey: credentialKey, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return &DeletionResult{Outcome: DeletionOutcome(resp.GetOutcome())}, nil -} diff --git a/sdk/go/openshell/v1/refresh_client_test.go b/sdk/go/openshell/v1/refresh_client_test.go deleted file mode 100644 index fc1d1306a5..0000000000 --- a/sdk/go/openshell/v1/refresh_client_test.go +++ /dev/null @@ -1,430 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "net" - "sync" - "testing" - "time" - - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" - "google.golang.org/protobuf/types/known/timestamppb" -) - -// --- Mock server for credential refresh --- - -type mockRefreshServer struct { - pb.UnimplementedOpenShellServer - mu sync.Mutex - statuses map[string]*pb.ProviderCredentialRefreshStatus // key: "provider/credentialKey" - getStatusErr error - configureErr error - rotateErr error - deleteErr error -} - -func newMockRefreshServer() *mockRefreshServer { - return &mockRefreshServer{ - statuses: make(map[string]*pb.ProviderCredentialRefreshStatus), - } -} - -func refreshKey(provider, credentialKey string) string { - return provider + "/" + credentialKey -} - -func (s *mockRefreshServer) GetProviderRefreshStatus(_ context.Context, req *pb.GetProviderRefreshStatusRequest) (*pb.GetProviderRefreshStatusResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.getStatusErr != nil { - return nil, s.getStatusErr - } - - var creds []*pb.ProviderCredentialRefreshStatus - if req.GetCredentialKey() != "" { - // Return specific credential - st, ok := s.statuses[refreshKey(req.GetProvider(), req.GetCredentialKey())] - if ok { - creds = append(creds, st) - } - } else { - // Return all credentials for provider - for key, st := range s.statuses { - if len(key) > len(req.GetProvider()) && key[:len(req.GetProvider())+1] == req.GetProvider()+"/" { - creds = append(creds, st) - } - } - } - return &pb.GetProviderRefreshStatusResponse{Credentials: creds}, nil -} - -func (s *mockRefreshServer) ConfigureProviderRefresh(_ context.Context, req *pb.ConfigureProviderRefreshRequest) (*pb.ConfigureProviderRefreshResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.configureErr != nil { - return nil, s.configureErr - } - - st := &pb.ProviderCredentialRefreshStatus{ - Provider: req.GetProvider(), - ProviderId: "prov-id-" + req.GetProvider(), - CredentialKey: req.GetCredentialKey(), - Strategy: req.GetStrategy(), - Status: "active", - ExpirationTime: req.GetExpirationTime(), - } - s.statuses[refreshKey(req.GetProvider(), req.GetCredentialKey())] = st - return &pb.ConfigureProviderRefreshResponse{Status: st}, nil -} - -func (s *mockRefreshServer) RotateProviderCredential(_ context.Context, req *pb.RotateProviderCredentialRequest) (*pb.RotateProviderCredentialResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.rotateErr != nil { - return nil, s.rotateErr - } - - key := refreshKey(req.GetProvider(), req.GetCredentialKey()) - st, ok := s.statuses[key] - if !ok { - return nil, status.Errorf(codes.NotFound, "refresh config %q not found", key) - } - st.Status = "rotated" - st.LastRefreshTime = timestamppb.Now() - return &pb.RotateProviderCredentialResponse{Status: st}, nil -} - -func (s *mockRefreshServer) DeleteProviderRefresh(_ context.Context, req *pb.DeleteProviderRefreshRequest) (*pb.DeleteProviderRefreshResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.deleteErr != nil { - return nil, s.deleteErr - } - - key := refreshKey(req.GetProvider(), req.GetCredentialKey()) - _, ok := s.statuses[key] - if !ok { - if !req.AllowMissing { - return nil, status.Error(codes.NotFound, "refresh configuration not found") - } - return &pb.DeleteProviderRefreshResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_ALREADY_ABSENT}, nil - } - delete(s.statuses, key) - return &pb.DeleteProviderRefreshResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_COMPLETED}, nil -} - -// --- Test setup --- - -func setupRefreshTest(t *testing.T, mock *mockRefreshServer) (*refreshClient, func()) { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - - return newRefreshClient(conn), func() { - _ = conn.Close() - srv.Stop() - } -} - -// --- GetStatus tests --- - -func TestRefreshGetStatus(t *testing.T) { - mock := newMockRefreshServer() - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - // Configure a credential first - cfg := &RefreshConfig{ - Provider: "openai", - CredentialKey: "api-key", - Strategy: RefreshStrategyOAuth2RefreshToken, - Material: map[string]string{"refresh_token": "tok-123"}, - } - _, err := client.Configure(context.Background(), "default", cfg) - require.NoError(t, err) - - // Get status for specific credential - statuses, err := client.GetStatus(context.Background(), "default", "openai", "api-key") - - require.NoError(t, err) - require.Len(t, statuses, 1) - assert.Equal(t, "openai", statuses[0].Provider) - assert.Equal(t, "api-key", statuses[0].CredentialKey) - assert.Equal(t, RefreshStrategyOAuth2RefreshToken, statuses[0].Strategy) - assert.Equal(t, "active", statuses[0].Status) -} - -func TestRefreshGetStatus_AllCredentials(t *testing.T) { - mock := newMockRefreshServer() - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - // Configure two credentials - _, err := client.Configure(context.Background(), "default", &RefreshConfig{ - Provider: "openai", - CredentialKey: "key-1", - Strategy: RefreshStrategyStatic, - }) - require.NoError(t, err) - _, err = client.Configure(context.Background(), "default", &RefreshConfig{ - Provider: "openai", - CredentialKey: "key-2", - Strategy: RefreshStrategyExternal, - }) - require.NoError(t, err) - - // Get all statuses (empty credentialKey) - statuses, err := client.GetStatus(context.Background(), "default", "openai", "") - - require.NoError(t, err) - assert.Len(t, statuses, 2) -} - -func TestRefreshGetStatus_Empty(t *testing.T) { - mock := newMockRefreshServer() - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - statuses, err := client.GetStatus(context.Background(), "default", "openai", "nonexistent") - - require.NoError(t, err) - assert.Empty(t, statuses) -} - -func TestRefreshGetStatus_Error(t *testing.T) { - mock := newMockRefreshServer() - mock.getStatusErr = status.Errorf(codes.Internal, "internal error") - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - statuses, err := client.GetStatus(context.Background(), "default", "openai", "key") - - assert.Nil(t, statuses) - require.Error(t, err) -} - -// --- Configure tests --- - -func TestRefreshConfigure(t *testing.T) { - mock := newMockRefreshServer() - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - expires := time.Date(2026, 12, 31, 23, 59, 59, 0, time.UTC) - cfg := &RefreshConfig{ - Provider: "openai", - CredentialKey: "api-key", - Strategy: RefreshStrategyOAuth2ClientCredentials, - Material: map[string]string{"client_id": "id-1", "client_secret": "sec-1"}, - SecretMaterialKeys: []string{"client_secret"}, - ExpiresAt: &expires, - } - - result, err := client.Configure(context.Background(), "default", cfg) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, "openai", result.Provider) - assert.Equal(t, "prov-id-openai", result.ProviderID) - assert.Equal(t, "api-key", result.CredentialKey) - assert.Equal(t, RefreshStrategyOAuth2ClientCredentials, result.Strategy) - assert.Equal(t, "active", result.Status) -} - -func TestRefreshConfigure_MinimalConfig(t *testing.T) { - mock := newMockRefreshServer() - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - cfg := &RefreshConfig{ - Provider: "anthropic", - CredentialKey: "key", - Strategy: RefreshStrategyStatic, - } - - result, err := client.Configure(context.Background(), "default", cfg) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, "anthropic", result.Provider) - assert.Equal(t, RefreshStrategyStatic, result.Strategy) -} - -func TestRefreshConfigure_Error(t *testing.T) { - mock := newMockRefreshServer() - mock.configureErr = status.Errorf(codes.InvalidArgument, "invalid config") - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - cfg := &RefreshConfig{ - Provider: "openai", - CredentialKey: "key", - Strategy: RefreshStrategyStatic, - } - - result, err := client.Configure(context.Background(), "default", cfg) - - assert.Nil(t, result) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -// --- Rotate tests --- - -func TestRefreshRotate(t *testing.T) { - mock := newMockRefreshServer() - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - // Configure first - _, err := client.Configure(context.Background(), "default", &RefreshConfig{ - Provider: "openai", - CredentialKey: "api-key", - Strategy: RefreshStrategyOAuth2RefreshToken, - }) - require.NoError(t, err) - - // Rotate - result, err := client.Rotate(context.Background(), "default", "openai", "api-key") - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, "rotated", result.Status) - assert.False(t, result.LastRefreshAt.IsZero()) -} - -func TestRefreshRotate_NotFound(t *testing.T) { - mock := newMockRefreshServer() - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - result, err := client.Rotate(context.Background(), "default", "openai", "nonexistent") - - assert.Nil(t, result) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestRefreshRotate_Error(t *testing.T) { - mock := newMockRefreshServer() - mock.rotateErr = status.Errorf(codes.Unavailable, "unavailable") - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - result, err := client.Rotate(context.Background(), "default", "openai", "key") - - assert.Nil(t, result) - require.Error(t, err) - assert.True(t, IsUnavailable(err)) -} - -// --- Delete tests --- - -func TestRefreshDelete(t *testing.T) { - mock := newMockRefreshServer() - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - // Configure first - _, err := client.Configure(context.Background(), "default", &RefreshConfig{ - Provider: "openai", - CredentialKey: "api-key", - Strategy: RefreshStrategyStatic, - }) - require.NoError(t, err) - - // Delete - deleted, err := client.Delete(context.Background(), "default", "openai", "api-key") - - require.NoError(t, err) - assert.Equal(t, DeletionCompleted, deleted.Outcome) - - // Verify it's gone - statuses, err := client.GetStatus(context.Background(), "default", "openai", "api-key") - require.NoError(t, err) - assert.Empty(t, statuses) -} - -func TestRefreshDelete_NotConfigured(t *testing.T) { - mock := newMockRefreshServer() - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - deleted, err := client.Delete(context.Background(), "default", "openai", "nonexistent", DeleteOptions{AllowMissing: true}) - - require.NoError(t, err) - assert.Equal(t, DeletionAlreadyAbsent, deleted.Outcome) -} - -func TestRefreshDelete_Error(t *testing.T) { - mock := newMockRefreshServer() - mock.deleteErr = status.Errorf(codes.Internal, "internal error") - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - deleted, err := client.Delete(context.Background(), "default", "openai", "key") - - assert.Nil(t, deleted) - require.Error(t, err) -} - -// --- Integration test: full lifecycle --- - -func TestRefreshLifecycle(t *testing.T) { - mock := newMockRefreshServer() - client, cleanup := setupRefreshTest(t, mock) - defer cleanup() - - ctx := context.Background() - - // 1. Configure - cfg := &RefreshConfig{ - Provider: "openai", - CredentialKey: "api-key", - Strategy: RefreshStrategyOAuth2RefreshToken, - Material: map[string]string{"refresh_token": "tok-123"}, - } - st, err := client.Configure(ctx, "default", cfg) - require.NoError(t, err) - assert.Equal(t, "active", st.Status) - - // 2. GetStatus - statuses, err := client.GetStatus(ctx, "default", "openai", "api-key") - require.NoError(t, err) - require.Len(t, statuses, 1) - - // 3. Rotate - rotated, err := client.Rotate(ctx, "default", "openai", "api-key") - require.NoError(t, err) - assert.Equal(t, "rotated", rotated.Status) - - // 4. Delete - deleted, err := client.Delete(ctx, "default", "openai", "api-key") - require.NoError(t, err) - assert.Equal(t, DeletionCompleted, deleted.Outcome) - - // 5. Verify removed - statuses, err = client.GetStatus(ctx, "default", "openai", "api-key") - require.NoError(t, err) - assert.Empty(t, statuses) -} diff --git a/sdk/go/openshell/v1/routing.go b/sdk/go/openshell/v1/routing.go deleted file mode 100644 index 8c90762024..0000000000 --- a/sdk/go/openshell/v1/routing.go +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "io" - "strings" - - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" -) - -// Metadata that lets an edge proxy send a sandbox's long-lived calls straight -// to the gateway replica holding its supervisor session. The headers and the -// retry rule are documented in docs/kubernetes/ingress.mdx. -const ( - ownerReplicaHeader = "x-openshell-owner" - ownerRequestHeader = "x-openshell-want-owner" - routeReplicaHeader = "x-openshell-replica" -) - -// ownerGetter is implemented by the gateway-backed sandbox client. -type ownerGetter interface { - getWithOwner(ctx context.Context, workspace, name string) (*Sandbox, string, error) -} - -// sandboxOwner checks that the sandbox exists and returns the replica that -// owns its supervisor session, or "" when the gateway reports none. -func sandboxOwner(ctx context.Context, sandboxes SandboxInterface, workspace, name string) (string, error) { - if g, ok := sandboxes.(ownerGetter); ok { - _, owner, err := g.getWithOwner(ctx, workspace, name) - return owner, err - } - _, err := sandboxes.Get(ctx, workspace, name) - return "", err -} - -// openRouted opens a stream tagged with the owner replica. It opens once more -// untagged when the proxy reports that the owner's route has no ready endpoints. -// A stale owner is safe: any replica relays the call to the real owner. -// With an owner, it returns only after the gateway answers, as the CLI does. -func openRouted[S grpc.ClientStream](ctx context.Context, owner string, open func(context.Context) (S, error)) (S, error) { - if owner == "" { - return open(ctx) - } - stream, err := answered(open(metadata.AppendToOutgoingContext(ctx, routeReplicaHeader, owner))) - if retryUnrouted(err) { - return answered(open(ctx)) - } - return stream, err -} - -// answered waits for the response headers. Nil metadata means the stream ended -// without headers, and RecvMsg then returns its status. -func answered[S grpc.ClientStream](stream S, err error) (S, error) { - if err == nil { - var md metadata.MD - if md, err = stream.Header(); err == nil && md == nil { - err = stream.RecvMsg(nil) - } - } - return stream, err -} - -// openBidiRouted is openRouted for a bidi stream whose first frame is sent on open. -func openBidiRouted[I, O any](ctx context.Context, owner string, first *I, open func(context.Context, ...grpc.CallOption) (grpc.BidiStreamingClient[I, O], error)) (grpc.BidiStreamingClient[I, O], error) { - return openRouted(ctx, owner, func(ctx context.Context) (grpc.BidiStreamingClient[I, O], error) { - s, err := open(ctx) - if err != nil { - return nil, err - } - // io.EOF means the stream already ended; answered reports its status. - if err := s.Send(first); err == io.EOF { - return answered(s, nil) - } else if err != nil { - return s, err - } - return s, nil - }) -} - -// retryUnrouted matches what Envoy Gateway answers for a replica route with no -// ready endpoints: UNAVAILABLE with no message. Gateway errors always carry one. -func retryUnrouted(err error) bool { - s, ok := status.FromError(err) - if !ok || s.Code() != codes.Unavailable { - return false - } - msg := s.Message() - return msg == "" || strings.Contains(msg, "no healthy upstream") || strings.Contains(msg, "connection failure") -} diff --git a/sdk/go/openshell/v1/routing_test.go b/sdk/go/openshell/v1/routing_test.go deleted file mode 100644 index 115ef924e7..0000000000 --- a/sdk/go/openshell/v1/routing_test.go +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "io" - "net" - "sync" - "testing" - - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" -) - -// routingServer reports owner from GetSandbox and records the replica header -// of every streaming call ("" when unrouted). Routed calls fail with routedErr. -type routingServer struct { - pb.UnimplementedOpenShellServer - owner string - routedErr error - - mu sync.Mutex - wantedBy []string - routes []string -} - -func (s *routingServer) GetSandbox(ctx context.Context, _ *pb.GetSandboxRequest) (*pb.SandboxResponse, error) { - md, _ := metadata.FromIncomingContext(ctx) - s.mu.Lock() - s.wantedBy = append(s.wantedBy, firstValue(md, ownerRequestHeader)) - s.mu.Unlock() - if s.owner != "" { - _ = grpc.SetHeader(ctx, metadata.Pairs(ownerReplicaHeader, s.owner)) - } - return &pb.SandboxResponse{Sandbox: &pb.Sandbox{}}, nil -} - -// route records the call and returns the error a routed call should fail with. -func (s *routingServer) route(ctx context.Context) error { - md, _ := metadata.FromIncomingContext(ctx) - replica := firstValue(md, routeReplicaHeader) - s.mu.Lock() - defer s.mu.Unlock() - s.routes = append(s.routes, replica) - if replica != "" { - return s.routedErr - } - return nil -} - -func (s *routingServer) ExecSandbox(_ *pb.ExecSandboxRequest, stream grpc.ServerStreamingServer[pb.ExecSandboxEvent]) error { - if err := s.route(stream.Context()); err != nil { - return err - } - return stream.Send(exitEvent()) -} - -func (s *routingServer) ExecSandboxInteractive(stream grpc.BidiStreamingServer[pb.ExecSandboxInput, pb.ExecSandboxEvent]) error { - if _, err := stream.Recv(); err != nil { - return err - } - if err := s.route(stream.Context()); err != nil { - return err - } - return stream.Send(exitEvent()) -} - -func (s *routingServer) ForwardTcp(stream grpc.BidiStreamingServer[pb.TcpForwardFrame, pb.TcpForwardFrame]) error { //nolint:revive // proto-generated method name - if _, err := stream.Recv(); err != nil { - return err - } - if err := s.route(stream.Context()); err != nil { - return err - } - return stream.Send(&pb.TcpForwardFrame{Payload: &pb.TcpForwardFrame_Data{Data: []byte("pong")}}) -} - -func exitEvent() *pb.ExecSandboxEvent { - return &pb.ExecSandboxEvent{Payload: &pb.ExecSandboxEvent_Exit{Exit: &pb.ExecSandboxExit{}}} -} - -func firstValue(md metadata.MD, key string) string { - if v := md.Get(key); len(v) > 0 { - return v[0] - } - return "" -} - -func routingClients(t *testing.T, srv *routingServer) (*execClient, *tcpClient) { - t.Helper() - lis := bufconn.Listen(bufSize) - gs := grpc.NewServer() - pb.RegisterOpenShellServer(gs, srv) - go func() { _ = gs.Serve(lis) }() - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { return lis.Dial() }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - t.Cleanup(func() { - _ = conn.Close() - gs.Stop() - }) - sandboxes := newSandboxClient(conn) - return newExecClient(conn, sandboxes), newTCPClient(conn, sandboxes, nil) -} - -func TestExecRoutesToOwner(t *testing.T) { - srv := &routingServer{owner: "openshell-1"} - exec, _ := routingClients(t, srv) - - _, err := exec.Run(context.Background(), "default", "box", []string{"true"}) - require.NoError(t, err) - - assert.Equal(t, []string{"1"}, srv.wantedBy) - assert.Equal(t, []string{"openshell-1"}, srv.routes) -} - -func TestExecWithoutOwnerIsUnrouted(t *testing.T) { - srv := &routingServer{} - exec, _ := routingClients(t, srv) - - _, err := exec.Run(context.Background(), "default", "box", []string{"true"}) - require.NoError(t, err) - - assert.Equal(t, []string{""}, srv.routes) -} - -func TestRoutedCallsRetryOnceWithoutHintWhenOwnerHasNoEndpoints(t *testing.T) { - srv := &routingServer{owner: "openshell-1", routedErr: status.Error(codes.Unavailable, "")} - exec, tcp := routingClients(t, srv) - ctx := context.Background() - - _, err := exec.Run(ctx, "default", "box", []string{"true"}) - require.NoError(t, err) - - stream, err := exec.Stream(ctx, "default", "box", []string{"true"}) - require.NoError(t, err) - code, err := stream.ExitCode() - require.NoError(t, err) - assert.Zero(t, code) - - session, err := exec.Interactive(ctx, "default", "box", []string{"sh"}, 80, 24) - require.NoError(t, err) - _, err = session.ExitCode() - require.NoError(t, err) - - conn, err := tcp.Forward(ctx, "default", "box", 8080) - require.NoError(t, err) - buf := make([]byte, 4) - _, err = io.ReadFull(conn, buf) - require.NoError(t, err) - assert.Equal(t, "pong", string(buf)) - _ = conn.Close() - - // Each call is attempted routed, then once unrouted. - assert.Equal(t, []string{ - "openshell-1", "", "openshell-1", "", "openshell-1", "", "openshell-1", "", - }, srv.routes) -} - -func TestRoutedCallDoesNotRetryOnGatewayError(t *testing.T) { - srv := &routingServer{owner: "openshell-1", routedErr: status.Error(codes.Unavailable, "supervisor not connected")} - exec, tcp := routingClients(t, srv) - ctx := context.Background() - - _, err := exec.Run(ctx, "default", "box", []string{"true"}) - require.Error(t, err) - _, err = tcp.Forward(ctx, "default", "box", 8080) - require.Error(t, err) - - assert.Equal(t, []string{"openshell-1", "openshell-1"}, srv.routes) -} - -func TestRetryUnrouted(t *testing.T) { - assert.True(t, retryUnrouted(status.Error(codes.Unavailable, ""))) - assert.True(t, retryUnrouted(status.Error(codes.Unavailable, "no healthy upstream"))) - assert.True(t, retryUnrouted(status.Error(codes.Unavailable, "upstream connect error: connection failure"))) - assert.False(t, retryUnrouted(status.Error(codes.Unavailable, "supervisor not connected"))) - assert.False(t, retryUnrouted(status.Error(codes.NotFound, ""))) - assert.False(t, retryUnrouted(io.EOF)) - assert.False(t, retryUnrouted(nil)) -} diff --git a/sdk/go/openshell/v1/sandbox.go b/sdk/go/openshell/v1/sandbox.go deleted file mode 100644 index 1e6c24cc9c..0000000000 --- a/sdk/go/openshell/v1/sandbox.go +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// Sandbox represents a sandbox instance. -type Sandbox = types.Sandbox - -// SandboxSpec holds the desired state of a sandbox. -type SandboxSpec = types.SandboxSpec - -// SandboxTemplate defines the container template for a sandbox. -type SandboxTemplate = types.SandboxTemplate - -// SandboxStatus holds the observed state of a sandbox. -type SandboxStatus = types.SandboxStatus - -// SandboxCondition describes an observed condition of a sandbox. -type SandboxCondition = types.SandboxCondition - -// EndpointStatus holds a configured tool endpoint and its last accepted network result. -type EndpointStatus = types.EndpointStatus - -// EndpointResult classifies the last accepted network result for a tool endpoint. -type EndpointResult = types.EndpointResult - -// EndpointResult values describe passive observations of actual traffic. -const ( - EndpointUnspecified = types.EndpointUnspecified - EndpointNoObservedExchange = types.EndpointNoObservedExchange - EndpointHTTPResponseReceived = types.EndpointHTTPResponseReceived - EndpointPolicyDenied = types.EndpointPolicyDenied - EndpointCredentialUnavailable = types.EndpointCredentialUnavailable - EndpointTLSFailed = types.EndpointTLSFailed - EndpointTransportFailed = types.EndpointTransportFailed - EndpointUpstreamRejected = types.EndpointUpstreamRejected -) - -// EndpointFailure explains a failed endpoint result with bounded values. -type EndpointFailure = types.EndpointFailure - -// TokenGrantFailureObservation records a refused grant's destination and cause. -type TokenGrantFailureObservation = types.TokenGrantFailureObservation - -// EndpointFailureDomain names the subsystem that explains an endpoint failure. -type EndpointFailureDomain = types.EndpointFailureDomain - -// EndpointFailureDomain values select the closed set that Code and Stage come from. -const ( - EndpointFailureDomainUnspecified = types.EndpointFailureDomainUnspecified - EndpointFailureDomainTokenGrant = types.EndpointFailureDomainTokenGrant -) - -// AttachProviderResult holds the result of attaching a provider to a sandbox. -type AttachProviderResult = types.AttachProviderResult - -// DetachProviderResult holds the result of detaching a provider from a sandbox. -type DetachProviderResult = types.DetachProviderResult - -// LogLine represents a single log entry from a sandbox. -type LogLine = types.LogLine - -// LogResult contains the result of a GetLogs call. -type LogResult = types.LogResult - -// LogOption configures a GetLogs call. -type LogOption = types.LogOption - -// WithLogLines sets the maximum number of log lines to return. -var WithLogLines = types.WithLogLines - -// WithLogSince filters logs to entries at or after the given time. -var WithLogSince = types.WithLogSince - -// WithLogSources filters logs by source (e.g., "gateway", "sandbox"). -var WithLogSources = types.WithLogSources - -// WithLogMinLevel sets the minimum log level to include. -var WithLogMinLevel = types.WithLogMinLevel - -// SandboxInterface defines lifecycle operations on sandboxes. -type SandboxInterface interface { - Create(ctx context.Context, workspace, name string, spec *SandboxSpec, opts ...CreateOption) (*Sandbox, error) - Get(ctx context.Context, workspace, name string) (*Sandbox, error) - List(workspace string, opts ...ListOptions) (*Pager[*Sandbox], error) - ListAll(ctx context.Context, workspace string, opts ...ListOptions) ([]*Sandbox, error) - Stop(ctx context.Context, workspace, name string) (*Sandbox, error) - Start(ctx context.Context, workspace, name string) (*Sandbox, error) - Delete(ctx context.Context, workspace, name string, opts ...DeleteOptions) (*DeletionResult, error) - AttachProvider(ctx context.Context, workspace, sandboxName, providerName string, expectedResourceVersion uint64) (*AttachProviderResult, error) - DetachProvider(ctx context.Context, workspace, sandboxName, providerName string, expectedResourceVersion uint64) (*DetachProviderResult, error) - // ListProviders returns a lazy pager over providers attached to a sandbox. - ListProviders(workspace, sandboxName string, opts ...ListOptions) (*Pager[*Provider], error) - // ListAllProviders exhausts ListProviders for callers that need every provider. - ListAllProviders(ctx context.Context, workspace, sandboxName string, opts ...ListOptions) ([]*Provider, error) - WaitReady(ctx context.Context, workspace, name string, opts ...WaitOptions) (*Sandbox, error) - WaitStopped(ctx context.Context, workspace, name string, opts ...WaitOptions) (*Sandbox, error) - Watch(ctx context.Context, workspace, name string, opts ...WatchOptions) (WatchInterface[*Sandbox], error) - GetLogs(ctx context.Context, workspace, sandboxName string, opts ...LogOption) (*LogResult, error) -} - -// SandboxTemplateCreateInterface defines additive sandbox creation from named -// workload templates without widening SandboxInterface. -type SandboxTemplateCreateInterface interface { - CreateFromTemplate(ctx context.Context, workspace, name, templateName string, spec *SandboxSpec, opts ...CreateOption) (*Sandbox, error) -} - -// SandboxSuspendResumeInterface defines sandbox suspend and resume without -// widening SandboxInterface. Suspend and resume require a compute driver and -// isolation backend that retain workload memory state. -type SandboxSuspendResumeInterface interface { - // Suspend freezes a Ready sandbox and retains its memory state. - Suspend(ctx context.Context, workspace, name string) (*Sandbox, error) - // Resume restores a Suspended sandbox in a new runtime generation. - Resume(ctx context.Context, workspace, name string, opts ...ResumeOptions) (*Sandbox, error) - // WaitSuspended polls until the sandbox reaches the Suspended phase. - WaitSuspended(ctx context.Context, workspace, name string, opts ...WaitOptions) (*Sandbox, error) -} diff --git a/sdk/go/openshell/v1/sandbox_client.go b/sdk/go/openshell/v1/sandbox_client.go deleted file mode 100644 index 5d042b2101..0000000000 --- a/sdk/go/openshell/v1/sandbox_client.go +++ /dev/null @@ -1,493 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "fmt" - "io" - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" -) - -const defaultPollInterval = 500 * time.Millisecond - -type sandboxClient struct { - client pb.OpenShellClient -} - -var _ SandboxInterface = (*sandboxClient)(nil) -var _ SandboxTemplateCreateInterface = (*sandboxClient)(nil) -var _ SandboxSuspendResumeInterface = (*sandboxClient)(nil) - -func newSandboxClient(conn grpc.ClientConnInterface) *sandboxClient { - return &sandboxClient{client: pb.NewOpenShellClient(conn)} -} - -func (s *sandboxClient) Create(ctx context.Context, workspace, name string, spec *SandboxSpec, opts ...CreateOption) (*Sandbox, error) { - protoSpec, err := converter.SandboxSpecToProtoChecked(spec) - if err != nil { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: err.Error()} - } - cfg := types.ApplyCreateOptions(opts) - req := &pb.CreateSandboxRequest{ - Name: name, - Spec: protoSpec, - Labels: converter.CopyStringMap(cfg.Labels()), - Annotations: converter.CopyStringMap(cfg.Annotations()), - WorkspaceScope: namedWorkspaceScope(workspace), - ServiceExposures: serviceExposuresToProto(cfg.ServiceExposures()), - } - resp, err := s.client.CreateSandbox(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - sandbox := converter.SandboxFromProto(resp.GetSandbox()) - if sandbox != nil { - sandbox.ServiceURLs = converter.CopyStringMap(resp.GetServiceUrls()) - } - return sandbox, nil -} - -func (s *sandboxClient) CreateFromTemplate(ctx context.Context, workspace, name, templateName string, spec *SandboxSpec, opts ...CreateOption) (*Sandbox, error) { - if templateName == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "template name is required"} - } - if err := validateTemplateCreateSpec(spec); err != nil { - return nil, err - } - protoSpec, err := converter.SandboxSpecToProtoChecked(spec) - if err != nil { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: err.Error()} - } - cfg := types.ApplyCreateOptions(opts) - req := &pb.CreateSandboxRequest{ - Name: name, - Spec: protoSpec, - Labels: converter.CopyStringMap(cfg.Labels()), - Annotations: converter.CopyStringMap(cfg.Annotations()), - WorkspaceScope: namedWorkspaceScope(workspace), - WorkloadTemplate: templateName, - ServiceExposures: serviceExposuresToProto(cfg.ServiceExposures()), - } - resp, err := s.client.CreateSandbox(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - sandbox := converter.SandboxFromProto(resp.GetSandbox()) - if sandbox != nil { - sandbox.ServiceURLs = converter.CopyStringMap(resp.GetServiceUrls()) - } - return sandbox, nil -} - -func serviceExposuresToProto(exposures []types.ServiceExposure) []*pb.SandboxServiceExposure { - result := make([]*pb.SandboxServiceExposure, 0, len(exposures)) - for _, exposure := range exposures { - result = append(result, &pb.SandboxServiceExposure{ - Service: exposure.Service, - TargetPort: exposure.TargetPort, - AuthorizationMode: serviceAuthorizationModeToProto(exposure.AuthorizationMode), - }) - } - return result -} - -func serviceAuthorizationModeToProto(mode types.ServiceAuthorizationMode) pb.ServiceAuthorizationMode { - if mode == 0 { - return pb.ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_STRIP - } - return pb.ServiceAuthorizationMode(mode) -} - -func validateTemplateCreateSpec(spec *SandboxSpec) error { - if spec == nil { - return nil - } - if spec.LogLevel != "" || len(spec.Environment) > 0 || spec.Template != nil || spec.GPU || spec.GPUCount != nil { - return &StatusError{Code: ErrorInvalidArgument, Message: "template creates only allow policy, providers, command, and tty in spec"} - } - return nil -} - -func (s *sandboxClient) Get(ctx context.Context, workspace, name string) (*Sandbox, error) { - return s.get(ctx, workspace, name) -} - -// getWithOwner is Get that also asks the gateway for the owner replica. -func (s *sandboxClient) getWithOwner(ctx context.Context, workspace, name string) (*Sandbox, string, error) { - var md metadata.MD - sb, err := s.get(metadata.AppendToOutgoingContext(ctx, ownerRequestHeader, "1"), workspace, name, grpc.Header(&md)) - if err != nil { - return nil, "", err - } - var owner string - if v := md.Get(ownerReplicaHeader); len(v) > 0 { - owner = v[0] - } - return sb, owner, nil -} - -func (s *sandboxClient) get(ctx context.Context, workspace, name string, opts ...grpc.CallOption) (*Sandbox, error) { - resp, err := s.client.GetSandbox(ctx, &pb.GetSandboxRequest{ - Name: name, - WorkspaceScope: namedWorkspaceScope(workspace), - }, opts...) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.SandboxFromProto(resp.GetSandbox()), nil -} - -func (s *sandboxClient) List(workspace string, opts ...ListOptions) (*Pager[*Sandbox], error) { - pageSize, err := listPageSize(opts) - if err != nil { - return nil, err - } - var pageToken, labelSelector string - var allWorkspaces bool - if len(opts) > 0 { - pageToken = opts[0].PageToken - labelSelector = opts[0].LabelSelector - allWorkspaces = opts[0].AllWorkspaces - } - workspaceScope := namedWorkspaceScope(workspace) - if allWorkspaces { - workspaceScope = allWorkspacesScope() - } - return newPager(pageToken, func(ctx context.Context, pageToken string) (*Page[*Sandbox], error) { - req := &pb.ListSandboxesRequest{ - WorkspaceScope: workspaceScope, - PageSize: pageSize, - PageToken: pageToken, - LabelSelector: labelSelector, - } - resp, err := s.client.ListSandboxes(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - sandboxes := make([]*Sandbox, 0, len(resp.GetSandboxes())) - for _, proto := range resp.GetSandboxes() { - sandboxes = append(sandboxes, converter.SandboxFromProto(proto)) - } - return &Page[*Sandbox]{Items: sandboxes, NextPageToken: resp.GetNextPageToken()}, nil - }), nil -} - -func (s *sandboxClient) ListAll(ctx context.Context, workspace string, opts ...ListOptions) ([]*Sandbox, error) { - pager, err := s.List(workspace, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -func (s *sandboxClient) Delete(ctx context.Context, workspace, name string, opts ...DeleteOptions) (*DeletionResult, error) { - resp, err := s.client.DeleteSandbox(ctx, &pb.DeleteSandboxRequest{ - AllowMissing: allowMissing(opts), - Name: name, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return &DeletionResult{Outcome: DeletionOutcome(resp.GetOutcome()), SandboxID: resp.GetSandboxId()}, nil -} - -func (s *sandboxClient) Stop(ctx context.Context, workspace, name string) (*Sandbox, error) { - resp, err := s.client.StopSandbox(ctx, &pb.StopSandboxRequest{ - Name: name, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.SandboxFromProto(resp.GetSandbox()), nil -} - -func (s *sandboxClient) Start(ctx context.Context, workspace, name string) (*Sandbox, error) { - resp, err := s.client.StartSandbox(ctx, &pb.StartSandboxRequest{ - Name: name, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.SandboxFromProto(resp.GetSandbox()), nil -} - -func (s *sandboxClient) Suspend(ctx context.Context, workspace, name string) (*Sandbox, error) { - resp, err := s.client.SuspendSandbox(ctx, &pb.SuspendSandboxRequest{ - Name: name, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.SandboxFromProto(resp.GetSandbox()), nil -} - -func (s *sandboxClient) Resume(ctx context.Context, workspace, name string, opts ...ResumeOptions) (*Sandbox, error) { - var options ResumeOptions - if len(opts) > 0 { - options = opts[0] - } - resp, err := s.client.ResumeSandbox(ctx, &pb.ResumeSandboxRequest{ - Name: name, - WorkspaceScope: namedWorkspaceScope(workspace), - RestartIfRestoreFails: options.RestartIfRestoreFails, - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.SandboxFromProto(resp.GetSandbox()), nil -} - -func (s *sandboxClient) AttachProvider(ctx context.Context, workspace, sandboxName, providerName string, expectedResourceVersion uint64) (*AttachProviderResult, error) { - resp, err := s.client.AttachSandboxProvider(ctx, &pb.AttachSandboxProviderRequest{ - Provider: providerName, - ExpectedResourceVersion: expectedResourceVersion, - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return &AttachProviderResult{ - Sandbox: converter.SandboxFromProto(resp.GetSandbox()), - Attached: resp.GetAttached(), - }, nil -} - -func (s *sandboxClient) DetachProvider(ctx context.Context, workspace, sandboxName, providerName string, expectedResourceVersion uint64) (*DetachProviderResult, error) { - resp, err := s.client.DetachSandboxProvider(ctx, &pb.DetachSandboxProviderRequest{ - Provider: providerName, - ExpectedResourceVersion: expectedResourceVersion, - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return &DetachProviderResult{ - Sandbox: converter.SandboxFromProto(resp.GetSandbox()), - Detached: resp.GetDetached(), - }, nil -} - -func (s *sandboxClient) ListProviders(workspace, sandboxName string, opts ...ListOptions) (*Pager[*Provider], error) { - pageSize, err := listPageSize(opts) - if err != nil { - return nil, err - } - pageToken := "" - if len(opts) > 0 { - pageToken = opts[0].PageToken - } - return newPager(pageToken, func(ctx context.Context, pageToken string) (*Page[*Provider], error) { - resp, err := s.client.ListSandboxProviders(ctx, &pb.ListSandboxProvidersRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - PageSize: pageSize, - PageToken: pageToken, - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - providers := make([]*Provider, 0, len(resp.GetProviders())) - for _, proto := range resp.GetProviders() { - providers = append(providers, converter.ProviderFromProto(proto)) - } - return &Page[*Provider]{Items: providers, NextPageToken: resp.GetNextPageToken()}, nil - }), nil -} - -func (s *sandboxClient) ListAllProviders(ctx context.Context, workspace, sandboxName string, opts ...ListOptions) ([]*Provider, error) { - pager, err := s.ListProviders(workspace, sandboxName, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -func (s *sandboxClient) WaitReady(ctx context.Context, workspace, name string, opts ...WaitOptions) (*Sandbox, error) { - return s.waitForPhase(ctx, workspace, name, SandboxReady, opts...) -} - -func (s *sandboxClient) WaitStopped(ctx context.Context, workspace, name string, opts ...WaitOptions) (*Sandbox, error) { - return s.waitForPhase(ctx, workspace, name, SandboxStopped, opts...) -} - -func (s *sandboxClient) WaitSuspended(ctx context.Context, workspace, name string, opts ...WaitOptions) (*Sandbox, error) { - return s.waitForPhase(ctx, workspace, name, SandboxSuspended, opts...) -} - -func (s *sandboxClient) waitForPhase(ctx context.Context, workspace, name string, target SandboxPhase, opts ...WaitOptions) (*Sandbox, error) { - interval := defaultPollInterval - if len(opts) > 0 && opts[0].PollInterval > 0 { - interval = opts[0].PollInterval - } - - sb, err := s.Get(ctx, workspace, name) - if err != nil { - return nil, err - } - - if result, termErr := checkTerminalPhase(sb, name, target); result != nil || termErr != nil { - return result, termErr - } - - ticker := time.NewTicker(interval) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return nil, contextError(ctx.Err()) - case <-ticker.C: - sb, err = s.Get(ctx, workspace, name) - if err != nil { - return nil, err - } - if result, termErr := checkTerminalPhase(sb, name, target); result != nil || termErr != nil { - return result, termErr - } - } - } -} - -func checkTerminalPhase(sb *Sandbox, name string, target SandboxPhase) (*Sandbox, error) { - if sb.Status.Phase == target { - return sb, nil - } - switch sb.Status.Phase { - case SandboxCompleted: - if target == SandboxReady { - return sb, nil - } - return nil, &StatusError{Code: ErrorInternal, Message: fmt.Sprintf("sandbox %q completed before reaching %s", name, target)} - case SandboxStopped: - return nil, &StatusError{Code: ErrorInternal, Message: fmt.Sprintf("sandbox %q stopped before reaching %s", name, target)} - case SandboxError: - return nil, &StatusError{Code: ErrorInternal, Message: fmt.Sprintf("sandbox %q is in error state", name)} - case SandboxDeleting: - return nil, &StatusError{Code: ErrorInternal, Message: fmt.Sprintf("sandbox %q is being deleted", name)} - case SandboxSuspending, SandboxSuspended: - if target == SandboxSuspended { - return nil, nil - } - return nil, &StatusError{Code: ErrorInternal, Message: fmt.Sprintf("sandbox %q is suspended; resume it before waiting for %s", name, target)} - default: - return nil, nil - } -} - -func (s *sandboxClient) Watch(ctx context.Context, workspace, name string, opts ...WatchOptions) (WatchInterface[*Sandbox], error) { - if name == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - - var watchOpts WatchOptions - if len(opts) > 0 { - watchOpts = opts[0] - } - if _, err := s.Get(ctx, workspace, name); err != nil { - return nil, err - } - - streamCtx, streamCancel := context.WithCancel(ctx) - stream, err := s.client.WatchSandbox(streamCtx, &pb.WatchSandboxRequest{ - Sandbox: name, - WorkspaceScope: namedWorkspaceScope(workspace), - FollowStatus: true, - StopOnTerminal: watchOpts.StopOnTerminal, - }) - if err != nil { - streamCancel() - return nil, converter.FromGRPCError(err) - } - - first, err := stream.Recv() - if err != nil { - streamCancel() - return nil, converter.FromGRPCError(err) - } - - ch := make(chan Event[*Sandbox], 64) - w := newWatcher(ch, streamCancel) - - go func() { - defer close(ch) - defer streamCancel() - ev := first - isFirst := true - for { - if sbPayload, ok := ev.Payload.(*pb.SandboxStreamEvent_Sandbox); ok && sbPayload.Sandbox != nil { - sandbox := converter.SandboxFromProto(sbPayload.Sandbox) - eventType := EventModified - if isFirst { - eventType = EventAdded - isFirst = false - } else if sandbox.Status.Phase == SandboxDeleting { - eventType = EventDeleted - } - select { - case ch <- Event[*Sandbox]{Type: eventType, Object: sandbox}: - case <-w.done: - return - } - if watchOpts.StopOnTerminal && (sandbox.Status.Phase == SandboxReady || sandbox.Status.Phase == SandboxCompleted || sandbox.Status.Phase == SandboxStopped || sandbox.Status.Phase == SandboxError) { - w.Stop() - return - } - } - var recvErr error - ev, recvErr = stream.Recv() - if recvErr != nil { - if recvErr != io.EOF { - select { - case <-w.done: - return - default: - } - select { - case ch <- Event[*Sandbox]{Type: EventError, Err: converter.FromGRPCError(recvErr)}: - case <-w.done: - } - } - return - } - } - }() - - return w, nil -} - -func (s *sandboxClient) GetLogs(ctx context.Context, workspace, sandboxName string, opts ...LogOption) (*LogResult, error) { - if _, err := s.Get(ctx, workspace, sandboxName); err != nil { - return nil, err - } - cfg := types.ApplyLogOptions(opts) - req := &pb.GetSandboxLogsRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - Lines: cfg.Lines(), - Sources: cfg.Sources(), - MinLevel: cfg.MinLevel(), - } - if !cfg.Since().IsZero() { - req.SinceTime = converter.TimestampFromTime(cfg.Since()) - } - - resp, err := s.client.GetSandboxLogs(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.LogResultFromProto(resp), nil -} diff --git a/sdk/go/openshell/v1/sandbox_client_test.go b/sdk/go/openshell/v1/sandbox_client_test.go deleted file mode 100644 index 8e2e83b50d..0000000000 --- a/sdk/go/openshell/v1/sandbox_client_test.go +++ /dev/null @@ -1,1544 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "net" - "sync" - "testing" - "time" - - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/timestamppb" -) - -type mockSandboxServer struct { - pb.UnimplementedOpenShellServer - mu sync.Mutex - sandboxes map[string]*pb.Sandbox - providers map[string][]*dm.Provider - createErr error - getErr error - listErr error - listPages [][]*pb.Sandbox - listRequests []*pb.ListSandboxesRequest - listProviderPages [][]*dm.Provider - listProviderRequests []*pb.ListSandboxProvidersRequest - deleteErr error - deleteResponse *pb.DeleteSandboxResponse - deleteRequest *pb.DeleteSandboxRequest - attachErr error - detachErr error - listProvErr error - createRequest *pb.CreateSandboxRequest - resumeRequest *pb.ResumeSandboxRequest - watchEvents []*pb.SandboxStreamEvent - watchErr error - watchPostEventsErr error - watchKeepOpen chan struct{} // if non-nil, WatchSandbox blocks after sending events until closed - watchRequest *pb.WatchSandboxRequest // recorded request - - // GetLogs fields - getLogsResp *pb.GetSandboxLogsResponse - getLogsErr error - getLogsRequest *pb.GetSandboxLogsRequest // recorded request -} - -func newMockSandboxServer() *mockSandboxServer { - return &mockSandboxServer{ - sandboxes: make(map[string]*pb.Sandbox), - providers: make(map[string][]*dm.Provider), - } -} - -func (s *mockSandboxServer) CreateSandbox(_ context.Context, req *pb.CreateSandboxRequest) (*pb.SandboxResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.createRequest = proto.Clone(req).(*pb.CreateSandboxRequest) - if s.createErr != nil { - return nil, s.createErr - } - sb := &pb.Sandbox{ - Metadata: &dm.ObjectMeta{ - Id: "sb-" + req.GetName(), - Name: req.GetName(), - CreatedTime: timestamppb.New(time.UnixMilli(1700000000000)), - Labels: req.GetLabels(), - ResourceVersion: 1, - }, - Spec: req.GetSpec(), - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - } - s.sandboxes[req.GetName()] = sb - serviceURLs := make(map[string]string, len(req.GetServiceExposures())) - for _, exposure := range req.GetServiceExposures() { - serviceURLs[exposure.GetService()] = "https://" + exposure.GetService() + ".example.test/" - } - return &pb.SandboxResponse{Sandbox: sb, ServiceUrls: serviceURLs}, nil -} - -func (s *mockSandboxServer) GetSandbox(_ context.Context, req *pb.GetSandboxRequest) (*pb.SandboxResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.getErr != nil { - return nil, s.getErr - } - name := req.GetName() - sb, ok := s.sandboxes[name] - if !ok { - return nil, status.Errorf(codes.NotFound, "sandbox %q not found", name) - } - cloned := proto.Clone(sb).(*pb.Sandbox) - return &pb.SandboxResponse{Sandbox: cloned}, nil -} - -func (s *mockSandboxServer) setPhase(name string, phase pb.SandboxPhase) { - s.mu.Lock() - defer s.mu.Unlock() - if sb, ok := s.sandboxes[name]; ok { - sb.Status.Phase = phase - } -} - -func (s *mockSandboxServer) ListSandboxes(_ context.Context, req *pb.ListSandboxesRequest) (*pb.ListSandboxesResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.listRequests = append(s.listRequests, proto.Clone(req).(*pb.ListSandboxesRequest)) - if s.listErr != nil { - return nil, s.listErr - } - if s.listPages != nil { - page := 0 - if req.GetPageToken() == "page-2" { - page = 1 - } - nextPageToken := "" - if page+1 < len(s.listPages) { - nextPageToken = "page-2" - } - return &pb.ListSandboxesResponse{ - Sandboxes: s.listPages[page], - NextPageToken: nextPageToken, - }, nil - } - var list []*pb.Sandbox - for _, sb := range s.sandboxes { - list = append(list, sb) - } - return &pb.ListSandboxesResponse{Sandboxes: list}, nil -} - -func (s *mockSandboxServer) DeleteSandbox(_ context.Context, req *pb.DeleteSandboxRequest) (*pb.DeleteSandboxResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.deleteRequest = req - if s.deleteResponse != nil { - return s.deleteResponse, nil - } - if s.deleteErr != nil { - return nil, s.deleteErr - } - name := req.GetName() - _, ok := s.sandboxes[name] - if !ok { - return nil, status.Errorf(codes.NotFound, "sandbox %q not found", name) - } - delete(s.sandboxes, name) - return &pb.DeleteSandboxResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_COMPLETED}, nil -} - -func (s *mockSandboxServer) StopSandbox(_ context.Context, req *pb.StopSandboxRequest) (*pb.SandboxResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - name := req.GetName() - sb, ok := s.sandboxes[name] - if !ok { - return nil, status.Errorf(codes.NotFound, "sandbox %q not found", name) - } - sb.Status.Phase = pb.SandboxPhase_SANDBOX_PHASE_STOPPED - return &pb.SandboxResponse{Sandbox: proto.Clone(sb).(*pb.Sandbox)}, nil -} - -func (s *mockSandboxServer) StartSandbox(_ context.Context, req *pb.StartSandboxRequest) (*pb.SandboxResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - name := req.GetName() - sb, ok := s.sandboxes[name] - if !ok { - return nil, status.Errorf(codes.NotFound, "sandbox %q not found", name) - } - sb.Status.Phase = pb.SandboxPhase_SANDBOX_PHASE_STARTING - return &pb.SandboxResponse{Sandbox: proto.Clone(sb).(*pb.Sandbox)}, nil -} - -func (s *mockSandboxServer) SuspendSandbox(_ context.Context, req *pb.SuspendSandboxRequest) (*pb.SandboxResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - name := req.GetName() - sb, ok := s.sandboxes[name] - if !ok { - return nil, status.Errorf(codes.NotFound, "sandbox %q not found", name) - } - sb.Status.Phase = pb.SandboxPhase_SANDBOX_PHASE_SUSPENDED - return &pb.SandboxResponse{Sandbox: proto.Clone(sb).(*pb.Sandbox)}, nil -} - -func (s *mockSandboxServer) ResumeSandbox(_ context.Context, req *pb.ResumeSandboxRequest) (*pb.SandboxResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.resumeRequest = req - name := req.GetName() - sb, ok := s.sandboxes[name] - if !ok { - return nil, status.Errorf(codes.NotFound, "sandbox %q not found", name) - } - sb.Status.Phase = pb.SandboxPhase_SANDBOX_PHASE_STARTING - return &pb.SandboxResponse{Sandbox: proto.Clone(sb).(*pb.Sandbox)}, nil -} - -func (s *mockSandboxServer) AttachSandboxProvider(_ context.Context, req *pb.AttachSandboxProviderRequest) (*pb.AttachSandboxProviderResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.attachErr != nil { - return nil, s.attachErr - } - name := req.GetSandbox() - sb, ok := s.sandboxes[name] - if !ok { - return nil, status.Errorf(codes.NotFound, "sandbox %q not found", name) - } - sb.Spec.Providers = append(sb.Spec.Providers, req.GetProvider()) - return &pb.AttachSandboxProviderResponse{Sandbox: sb, Attached: true}, nil -} - -func (s *mockSandboxServer) DetachSandboxProvider(_ context.Context, req *pb.DetachSandboxProviderRequest) (*pb.DetachSandboxProviderResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.detachErr != nil { - return nil, s.detachErr - } - name := req.GetSandbox() - sb, ok := s.sandboxes[name] - if !ok { - return nil, status.Errorf(codes.NotFound, "sandbox %q not found", name) - } - return &pb.DetachSandboxProviderResponse{Sandbox: sb, Detached: true}, nil -} - -func (s *mockSandboxServer) ListSandboxProviders(_ context.Context, req *pb.ListSandboxProvidersRequest) (*pb.ListSandboxProvidersResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.listProvErr != nil { - return nil, s.listProvErr - } - s.listProviderRequests = append(s.listProviderRequests, proto.Clone(req).(*pb.ListSandboxProvidersRequest)) - if s.listProviderPages != nil { - page := 0 - if req.GetPageToken() == "page-2" { - page = 1 - } - nextPageToken := "" - if page+1 < len(s.listProviderPages) { - nextPageToken = "page-2" - } - return &pb.ListSandboxProvidersResponse{ - Providers: s.listProviderPages[page], - NextPageToken: nextPageToken, - }, nil - } - provs := s.providers[req.GetSandbox()] - return &pb.ListSandboxProvidersResponse{Providers: provs}, nil -} - -func (s *mockSandboxServer) WatchSandbox(req *pb.WatchSandboxRequest, stream grpc.ServerStreamingServer[pb.SandboxStreamEvent]) error { - s.mu.Lock() - s.watchRequest = req - s.mu.Unlock() - if s.watchErr != nil { - return s.watchErr - } - s.mu.Lock() - events := make([]*pb.SandboxStreamEvent, len(s.watchEvents)) - copy(events, s.watchEvents) - keepOpen := s.watchKeepOpen - s.mu.Unlock() - for _, ev := range events { - if err := stream.Send(ev); err != nil { - return err - } - } - if s.watchPostEventsErr != nil { - return s.watchPostEventsErr - } - // If watchKeepOpen is set, block until it is closed (simulates long-running stream) - if keepOpen != nil { - <-keepOpen - } - return nil -} - -func (s *mockSandboxServer) GetSandboxLogs(_ context.Context, req *pb.GetSandboxLogsRequest) (*pb.GetSandboxLogsResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.getLogsRequest = req - if s.getLogsErr != nil { - return nil, s.getLogsErr - } - if s.getLogsResp != nil { - return s.getLogsResp, nil - } - return &pb.GetSandboxLogsResponse{}, nil -} - -func setupSandboxTest(t *testing.T, mock *mockSandboxServer) (*sandboxClient, func()) { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - - return newSandboxClient(conn), func() { - _ = conn.Close() - srv.Stop() - } -} - -// --- T029: Sandbox CRUD tests --- - -func TestSandboxCreate(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - spec := &SandboxSpec{ - LogLevel: "debug", - Environment: map[string]string{"FOO": "bar"}, - Providers: []string{"claude"}, - } - labels := map[string]string{"env": "dev"} - - result, err := client.Create( - context.Background(), - "default", - "my-sandbox", - spec, - WithLabels(labels), - WithServiceExposures( - ServiceExposure{TargetPort: 4500}, - ServiceExposure{ - Service: "metrics", - TargetPort: 9090, - AuthorizationMode: ServiceAuthorizationModeBearerPassthrough, - }, - ), - ) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, "my-sandbox", result.Name) - assert.Equal(t, "sb-my-sandbox", result.ID) - assert.Equal(t, map[string]string{"env": "dev"}, result.Labels) - assert.Equal(t, SandboxProvisioning, result.Status.Phase) - assert.Equal(t, map[string]string{ - "": "https://.example.test/", - "metrics": "https://metrics.example.test/", - }, result.ServiceURLs) - require.Len(t, mock.createRequest.GetServiceExposures(), 2) - assert.Equal(t, uint32(4500), mock.createRequest.GetServiceExposures()[0].GetTargetPort()) - assert.Equal(t, pb.ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_STRIP, mock.createRequest.GetServiceExposures()[0].GetAuthorizationMode()) - assert.Equal(t, "metrics", mock.createRequest.GetServiceExposures()[1].GetService()) - assert.Equal(t, pb.ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_BEARER_PASSTHROUGH, mock.createRequest.GetServiceExposures()[1].GetAuthorizationMode()) -} - -// Callers that passed nil for the old positional labels parameter still compile -// against the variadic signature, so the nil option must be ignored rather than -// invoked. -func TestSandboxCreate_LegacyTrailingNil(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.Create(context.Background(), "default", "legacy-sandbox", &SandboxSpec{}, nil) - - require.NoError(t, err) - require.NotNil(t, result) - - mock.mu.Lock() - defer mock.mu.Unlock() - require.NotNil(t, mock.createRequest) - assert.Empty(t, mock.createRequest.Labels) - assert.Empty(t, mock.createRequest.Annotations) -} - -func TestSandboxCreate_WithAnnotations(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - annotations := map[string]string{"purpose": "ci-test"} - result, err := client.Create(context.Background(), "default", "ann-sandbox", &SandboxSpec{}, WithAnnotations(annotations)) - - require.NoError(t, err) - require.NotNil(t, result) - - mock.mu.Lock() - defer mock.mu.Unlock() - require.NotNil(t, mock.createRequest) - assert.Equal(t, map[string]string{"purpose": "ci-test"}, mock.createRequest.Annotations) -} - -func TestSandboxCreate_WithLabelsAndAnnotations(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - labels := map[string]string{"env": "staging"} - annotations := map[string]string{"owner": "team-a"} - result, err := client.Create(context.Background(), "default", "both-sandbox", &SandboxSpec{}, - WithLabels(labels), WithAnnotations(annotations)) - - require.NoError(t, err) - require.NotNil(t, result) - - mock.mu.Lock() - defer mock.mu.Unlock() - require.NotNil(t, mock.createRequest) - assert.Equal(t, map[string]string{"env": "staging"}, mock.createRequest.Labels) - assert.Equal(t, map[string]string{"owner": "team-a"}, mock.createRequest.Annotations) -} - -func TestSandboxCreate_DefaultGPURequest(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.Create(context.Background(), "default", "gpu-sandbox", &SandboxSpec{ - GPU: true, - }) - - require.NoError(t, err) - require.NotNil(t, result) - assert.True(t, result.Spec.GPU) - assert.Nil(t, result.Spec.GPUCount) - - mock.mu.Lock() - defer mock.mu.Unlock() - require.NotNil(t, mock.createRequest) - require.NotNil(t, mock.createRequest.Spec) - require.NotNil(t, mock.createRequest.Spec.ResourceRequirements) - require.NotNil(t, mock.createRequest.Spec.ResourceRequirements.Gpu) - assert.Nil(t, mock.createRequest.Spec.ResourceRequirements.Gpu.Count) -} - -func TestSandboxCreate_RejectsUnrepresentableResourcesBeforeRPC(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.Create(context.Background(), "default", "bad", &SandboxSpec{ - Template: &SandboxTemplate{Resources: map[string]any{"invalid": make(chan int)}}, - }) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) - mock.mu.Lock() - defer mock.mu.Unlock() - assert.Empty(t, mock.sandboxes) -} - -func TestSandboxCreateFromTemplateRejectsGPUOverrideBeforeRPC(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.CreateFromTemplate(context.Background(), "default", "bad", "gpu-kata", &SandboxSpec{ - GPU: true, - }) - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) - mock.mu.Lock() - defer mock.mu.Unlock() - assert.Nil(t, mock.createRequest) -} - -func TestSandboxCreateFromTemplateSendsCommandAndTTY(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.CreateFromTemplate(context.Background(), "default", "job-1", "gpu-kata", &SandboxSpec{ - Providers: []string{"github"}, - Command: []string{"/opt/worker", "--serve"}, - TTY: true, - }, WithLabels(map[string]string{"team": "runtime"})) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, []string{"/opt/worker", "--serve"}, result.Spec.Command) - assert.True(t, result.Spec.TTY) - - mock.mu.Lock() - defer mock.mu.Unlock() - require.NotNil(t, mock.createRequest) - assert.Equal(t, "gpu-kata", mock.createRequest.GetWorkloadTemplate()) - require.NotNil(t, mock.createRequest.GetSpec()) - assert.Equal(t, []string{"/opt/worker", "--serve"}, mock.createRequest.GetSpec().GetCommand()) - assert.True(t, mock.createRequest.GetSpec().GetTty()) -} - -func TestSandboxCreate_AlreadyExists(t *testing.T) { - mock := newMockSandboxServer() - mock.createErr = status.Error(codes.AlreadyExists, "sandbox already exists") - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.Create(context.Background(), "default", "dup", &SandboxSpec{}) - - require.Error(t, err) - assert.True(t, IsAlreadyExists(err)) -} - -func TestSandboxGet(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["existing"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "sb-1", Name: "existing", ResourceVersion: 5}, - Spec: &pb.SandboxSpec{LogLevel: "info"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.Get(context.Background(), "default", "existing") - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, "existing", result.Name) - assert.Equal(t, "sb-1", result.ID) - assert.Equal(t, uint64(5), result.ResourceVersion) - assert.Equal(t, SandboxReady, result.Status.Phase) -} - -func TestSandboxGet_NotFound(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.Get(context.Background(), "default", "nonexistent") - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestSandboxList(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["sb1"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - mock.sandboxes["sb2"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb2"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.ListAll(context.Background(), "default") - - require.NoError(t, err) - assert.Len(t, result, 2) -} - -func TestSandboxList_Empty(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.ListAll(context.Background(), "default") - - require.NoError(t, err) - assert.NotNil(t, result) - assert.Empty(t, result) -} - -func TestSandboxListAll_SelectsAllWorkspaces(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - sandboxes, err := client.ListAll(context.Background(), "", ListOptions{ - PageSize: 10, - AllWorkspaces: true, - }) - - require.NoError(t, err) - assert.Empty(t, sandboxes) - require.Len(t, mock.listRequests, 1) - assert.Equal(t, int32(10), mock.listRequests[0].GetPageSize()) - assert.NotNil(t, mock.listRequests[0].GetWorkspaceScope().GetAllWorkspaces()) -} - -func TestSandboxList_WithOptions(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["sb1"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.ListAll(context.Background(), "default", ListOptions{PageSize: 10}) - - require.NoError(t, err) - assert.Len(t, result, 1) -} - -func TestSandboxList_FollowsContinuationTokens(t *testing.T) { - mock := newMockSandboxServer() - mock.listPages = [][]*pb.Sandbox{ - {{Metadata: &dm.ObjectMeta{Name: "first"}}}, - {{Metadata: &dm.ObjectMeta{Name: "second"}}}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.ListAll(context.Background(), "default", ListOptions{ - PageSize: 1, - LabelSelector: "team=core", - }) - - require.NoError(t, err) - require.Len(t, result, 2) - assert.Equal(t, "first", result[0].Name) - assert.Equal(t, "second", result[1].Name) - require.Len(t, mock.listRequests, 2) - assert.Empty(t, mock.listRequests[0].GetPageToken()) - assert.Equal(t, "page-2", mock.listRequests[1].GetPageToken()) - assert.Equal(t, int32(1), mock.listRequests[1].GetPageSize()) - assert.Equal(t, "team=core", mock.listRequests[1].GetLabelSelector()) -} - -func TestSandboxDelete(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["deleteme"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "deleteme"}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.Delete(context.Background(), "default", "deleteme") - - require.NoError(t, err) - assert.Empty(t, mock.sandboxes["deleteme"]) -} - -func TestSandboxDelete_OutcomesAndOptions(t *testing.T) { - for _, outcome := range []int32{0, 1, 2, 3, 99} { - mock := newMockSandboxServer() - mock.deleteResponse = &pb.DeleteSandboxResponse{Outcome: pb.DeletionOutcome(outcome), SandboxId: "original-id"} - client, cleanup := setupSandboxTest(t, mock) - t.Cleanup(cleanup) - result, err := client.Delete(context.Background(), "default", "sandbox", DeleteOptions{AllowMissing: true}) - require.NoError(t, err) - assert.Equal(t, DeletionOutcome(outcome), result.Outcome) - assert.Equal(t, "original-id", result.SandboxID) - assert.True(t, mock.deleteRequest.GetAllowMissing()) - } -} - -func TestSandboxDelete_NotFound(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.Delete(context.Background(), "default", "nonexistent") - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestSandboxStopAndStart(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["lifecycle"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "lifecycle", Workspace: "team-a"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - stopped, err := client.Stop(context.Background(), "team-a", "lifecycle") - require.NoError(t, err) - assert.Equal(t, SandboxStopped, stopped.Status.Phase) - - starting, err := client.Start(context.Background(), "team-a", "lifecycle") - require.NoError(t, err) - assert.Equal(t, SandboxStarting, starting.Status.Phase) -} - -func TestSandboxSuspendAndResume(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["frozen"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "frozen", Workspace: "team-a"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - suspended, err := client.Suspend(context.Background(), "team-a", "frozen") - require.NoError(t, err) - assert.Equal(t, SandboxSuspended, suspended.Status.Phase) - - waited, err := client.WaitSuspended(context.Background(), "team-a", "frozen") - require.NoError(t, err) - assert.Equal(t, SandboxSuspended, waited.Status.Phase) - - _, err = client.WaitReady(context.Background(), "team-a", "frozen") - require.Error(t, err) - assert.Contains(t, err.Error(), "suspended") - - resumed, err := client.Resume(context.Background(), "team-a", "frozen", ResumeOptions{RestartIfRestoreFails: true}) - require.NoError(t, err) - assert.Equal(t, SandboxStarting, resumed.Status.Phase) - mock.mu.Lock() - assert.True(t, mock.resumeRequest.GetRestartIfRestoreFails()) - assert.Equal(t, "frozen", mock.resumeRequest.GetName()) - mock.mu.Unlock() -} - -// --- T030: AttachProvider, DetachProvider, ListProviders tests --- - -func TestSandboxAttachProvider(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["my-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "my-sb", ResourceVersion: 2}, - Spec: &pb.SandboxSpec{Providers: []string{"existing-prov"}}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.AttachProvider(context.Background(), "default", "my-sb", "new-prov", 2) - - require.NoError(t, err) - require.NotNil(t, result) - assert.True(t, result.Attached) - require.NotNil(t, result.Sandbox) - assert.Equal(t, "my-sb", result.Sandbox.Name) -} - -func TestSandboxAttachProvider_NotFound(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.AttachProvider(context.Background(), "default", "missing", "prov", 1) - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestSandboxAttachProvider_Error(t *testing.T) { - mock := newMockSandboxServer() - mock.attachErr = status.Error(codes.InvalidArgument, "bad version") - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.AttachProvider(context.Background(), "default", "sb", "prov", 99) - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestSandboxDetachProvider(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["my-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "my-sb", ResourceVersion: 3}, - Spec: &pb.SandboxSpec{Providers: []string{"prov-a", "prov-b"}}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.DetachProvider(context.Background(), "default", "my-sb", "prov-a", 3) - - require.NoError(t, err) - require.NotNil(t, result) - assert.True(t, result.Detached) - require.NotNil(t, result.Sandbox) - assert.Equal(t, "my-sb", result.Sandbox.Name) -} - -func TestSandboxDetachProvider_NotFound(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.DetachProvider(context.Background(), "default", "missing", "prov", 1) - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestSandboxListProviders(t *testing.T) { - mock := newMockSandboxServer() - mock.listProviderPages = [][]*dm.Provider{{ - {Metadata: &dm.ObjectMeta{Name: "claude-prov"}, Type: "claude"}, - }, { - {Metadata: &dm.ObjectMeta{Name: "github-prov"}, Type: "github"}, - }} - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - pager, err := client.ListProviders("default", "my-sb", ListOptions{PageSize: 1}) - - require.NoError(t, err) - page, err := pager.NextPage(context.Background()) - require.NoError(t, err) - require.NotNil(t, page) - assert.Len(t, page.Items, 1) - assert.Equal(t, "page-2", page.NextPageToken) - require.Len(t, mock.listProviderRequests, 1) - assert.Equal(t, "", mock.listProviderRequests[0].GetPageToken()) - assert.Equal(t, int32(1), mock.listProviderRequests[0].GetPageSize()) -} - -func TestSandboxListAllProviders(t *testing.T) { - mock := newMockSandboxServer() - mock.listProviderPages = [][]*dm.Provider{{ - {Metadata: &dm.ObjectMeta{Name: "claude-prov"}, Type: "claude"}, - }, { - {Metadata: &dm.ObjectMeta{Name: "github-prov"}, Type: "github"}, - }} - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.ListAllProviders(context.Background(), "default", "my-sb", ListOptions{PageSize: 1}) - - require.NoError(t, err) - assert.Len(t, result, 2) - require.Len(t, mock.listProviderRequests, 2) - assert.Equal(t, "", mock.listProviderRequests[0].GetPageToken()) - assert.Equal(t, "page-2", mock.listProviderRequests[1].GetPageToken()) -} - -func TestSandboxListProviders_Empty(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.ListAllProviders(context.Background(), "default", "empty-sb") - - require.NoError(t, err) - assert.Empty(t, result) -} - -func TestSandboxListProviders_Error(t *testing.T) { - mock := newMockSandboxServer() - mock.listProvErr = status.Error(codes.Unavailable, "service down") - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - pager, err := client.ListProviders("default", "sb") - require.NoError(t, err) - _, err = pager.NextPage(context.Background()) - - require.Error(t, err) - assert.True(t, IsUnavailable(err)) -} - -// --- T031: WaitReady tests --- - -func TestSandboxWaitStopped_AlreadyStopped(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["sleeping"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sleeping"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_STOPPED}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.WaitStopped(context.Background(), "default", "sleeping") - require.NoError(t, err) - assert.Equal(t, SandboxStopped, result.Status.Phase) -} - -func TestSandboxWaitReady_AlreadyReady(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["ready-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "ready-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.WaitReady(context.Background(), "default", "ready-sb") - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, "ready-sb", result.Name) - assert.Equal(t, SandboxReady, result.Status.Phase) -} - -func TestSandboxWaitReady_BecomesReady(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["pending-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "pending-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - go func() { - time.Sleep(50 * time.Millisecond) - mock.setPhase("pending-sb", pb.SandboxPhase_SANDBOX_PHASE_READY) - }() - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - result, err := client.WaitReady(ctx, "default", "pending-sb", WaitOptions{PollInterval: 20 * time.Millisecond}) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, SandboxReady, result.Status.Phase) -} - -func TestSandboxWaitReady_ContextTimeout(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["stuck-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "stuck-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) - defer cancel() - - _, err := client.WaitReady(ctx, "default", "stuck-sb", WaitOptions{PollInterval: 20 * time.Millisecond}) - - require.Error(t, err) - assert.True(t, IsDeadlineExceeded(err), "WaitReady must wrap context.DeadlineExceeded in StatusError") -} - -func TestSandboxWaitReady_ContextCancelled(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["cancel-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "cancel-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - ctx, cancel := context.WithCancel(context.Background()) - go func() { - time.Sleep(50 * time.Millisecond) - cancel() - }() - - _, err := client.WaitReady(ctx, "default", "cancel-sb", WaitOptions{PollInterval: 20 * time.Millisecond}) - - require.Error(t, err) - assert.True(t, IsCancelled(err), "WaitReady must wrap context.Canceled in StatusError") -} - -func TestSandboxWaitReady_SandboxFailed(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["fail-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "fail-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_ERROR}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.WaitReady(context.Background(), "default", "fail-sb") - - require.Error(t, err) -} - -func TestSandboxWaitReady_SandboxCompleted(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["complete-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "complete-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_COMPLETED}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.WaitReady(context.Background(), "default", "complete-sb") - - require.NoError(t, err) - assert.Equal(t, SandboxCompleted, result.Status.Phase) -} - -func TestSandboxWaitReady_SandboxStopped(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["stopped-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "stopped-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_STOPPED}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.WaitReady(context.Background(), "default", "stopped-sb") - - require.Error(t, err) - assert.Contains(t, err.Error(), "stopped") -} - -func TestSandboxWaitReady_SandboxDeleting(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["deleting-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "deleting-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_DELETING}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.WaitReady(context.Background(), "default", "deleting-sb") - - require.Error(t, err) - assert.Contains(t, err.Error(), "being deleted") -} - -func TestSandboxWaitReady_BecomesDeleting(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["del-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "del-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - go func() { - time.Sleep(50 * time.Millisecond) - mock.setPhase("del-sb", pb.SandboxPhase_SANDBOX_PHASE_DELETING) - }() - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - _, err := client.WaitReady(ctx, "default", "del-sb", WaitOptions{PollInterval: 20 * time.Millisecond}) - - require.Error(t, err) - assert.Contains(t, err.Error(), "being deleted") -} - -func TestSandboxWaitReady_NotFound(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.WaitReady(context.Background(), "default", "nonexistent") - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -// --- T039: Watch integration tests --- - -func TestSandboxWatch_ReceivesEvents(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["sb-1"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "id-1", Name: "sb-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - } - mock.watchEvents = []*pb.SandboxStreamEvent{ - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb-1", Id: "id-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - }}}, - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb-1", Id: "id-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - }}}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - w, err := client.Watch(context.Background(), "default", "sb-1") - require.NoError(t, err) - defer w.Stop() - - ev1 := <-w.ResultChan() - assert.Equal(t, EventAdded, ev1.Type) - require.NotNil(t, ev1.Object) - assert.Equal(t, "sb-1", ev1.Object.Name) - assert.Equal(t, SandboxProvisioning, ev1.Object.Status.Phase) - - ev2 := <-w.ResultChan() - assert.Equal(t, EventModified, ev2.Type) - assert.Equal(t, SandboxReady, ev2.Object.Status.Phase) -} - -func TestSandboxWatch_FiltersSandboxEventsOnly(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["sb-1"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "id-1", Name: "sb-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - mock.watchEvents = []*pb.SandboxStreamEvent{ - {Payload: &pb.SandboxStreamEvent_Log{Log: &pb.SandboxLogLine{Message: "some log"}}}, - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - }}}, - {Payload: &pb.SandboxStreamEvent_Warning{Warning: &pb.SandboxStreamWarning{Message: "warn"}}}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - w, err := client.Watch(context.Background(), "default", "sb-1") - require.NoError(t, err) - defer w.Stop() - - ev := <-w.ResultChan() - assert.Equal(t, EventAdded, ev.Type) - assert.Equal(t, "sb-1", ev.Object.Name) - - // Stream ends after server sends all events; channel should close - select { - case _, ok := <-w.ResultChan(): - assert.False(t, ok, "channel should close after stream ends") - case <-time.After(time.Second): - t.Fatal("timed out waiting for channel close") - } -} - -func TestSandboxWatch_StopCancelsStream(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["sb-1"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "id-1", Name: "sb-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - mock.watchEvents = []*pb.SandboxStreamEvent{ - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - }}}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - w, err := client.Watch(context.Background(), "default", "sb-1") - require.NoError(t, err) - - <-w.ResultChan() - w.Stop() - - select { - case _, ok := <-w.ResultChan(): - assert.False(t, ok, "channel should be closed after Stop") - case <-time.After(time.Second): - t.Fatal("timed out waiting for channel close after Stop") - } -} - -func TestSandboxWatch_RPCError(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["watch-err"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "id-watch-err", Name: "watch-err"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - mock.watchErr = status.Error(codes.Unavailable, "stream unavailable") - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.Watch(context.Background(), "default", "watch-err") - - require.Error(t, err) - assert.True(t, IsUnavailable(err)) -} - -func TestSandboxWatch_MidStreamErrorDeliveredAsStatusError(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["sb-1"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "id-1", Name: "sb-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - } - mock.watchEvents = []*pb.SandboxStreamEvent{ - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb-1", Id: "id-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - }}}, - } - mock.watchPostEventsErr = status.Error(codes.Unavailable, "connection lost") - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - w, err := client.Watch(context.Background(), "default", "sb-1") - require.NoError(t, err) - defer w.Stop() - - ev1 := <-w.ResultChan() - assert.Equal(t, EventAdded, ev1.Type) - - ev2 := <-w.ResultChan() - assert.Equal(t, EventError, ev2.Type) - require.Error(t, ev2.Err) - assert.True(t, IsUnavailable(ev2.Err), "mid-stream error should be converted to StatusError") -} - -// --- T016: Watch name-to-ID resolution verification tests --- - -func TestSandboxWatch_UsesName(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["my-sandbox"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "resolved-id-123", Name: "my-sandbox"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - } - mock.watchKeepOpen = make(chan struct{}) - defer close(mock.watchKeepOpen) - mock.watchEvents = []*pb.SandboxStreamEvent{ - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "my-sandbox", Id: "resolved-id-123"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - }}}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - w, err := client.Watch(context.Background(), "default", "my-sandbox") - require.NoError(t, err) - defer w.Stop() - - // Verify the WatchSandboxRequest reference contains the resolved ID, not the name. - mock.mu.Lock() - req := mock.watchRequest - mock.mu.Unlock() - require.NotNil(t, req) - assert.Equal(t, "my-sandbox", req.GetSandbox()) -} - -func TestSandboxWatch_ResolutionError(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.Watch(context.Background(), "default", "nonexistent") - - require.Error(t, err) - assert.True(t, IsNotFound(err), "Watch should return NotFound when sandbox name cannot be resolved") -} - -func TestSandboxWatch_EmptySandboxName(t *testing.T) { - mock := newMockSandboxServer() - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.Watch(context.Background(), "default", "") - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err), "Watch should reject empty sandbox name") -} - -// --- T023/T024: StopOnTerminal watch tests --- - -func TestSandboxWatch_StopOnTerminal_Ready(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["sb-1"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "id-1", Name: "sb-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - } - mock.watchKeepOpen = make(chan struct{}) - defer close(mock.watchKeepOpen) - mock.watchEvents = []*pb.SandboxStreamEvent{ - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb-1", Id: "id-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - }}}, - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb-1", Id: "id-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - }}}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - w, err := client.Watch(context.Background(), "default", "sb-1", WatchOptions{StopOnTerminal: true}) - require.NoError(t, err) - defer w.Stop() - - // Should receive the Provisioning event - ev1 := <-w.ResultChan() - assert.Equal(t, EventAdded, ev1.Type) - assert.Equal(t, SandboxProvisioning, ev1.Object.Status.Phase) - - // Should receive the Ready event (terminal) - ev2 := <-w.ResultChan() - assert.Equal(t, EventModified, ev2.Type) - assert.Equal(t, SandboxReady, ev2.Object.Status.Phase) - - // Channel should close automatically after terminal event (stream is still open) - select { - case _, ok := <-w.ResultChan(): - assert.False(t, ok, "channel should close after terminal Ready event") - case <-time.After(time.Second): - t.Fatal("timed out waiting for channel close after terminal Ready event") - } -} - -func TestSandboxWatch_StopOnTerminal_Error(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["sb-1"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "id-1", Name: "sb-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - } - mock.watchKeepOpen = make(chan struct{}) - defer close(mock.watchKeepOpen) - mock.watchEvents = []*pb.SandboxStreamEvent{ - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb-1", Id: "id-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - }}}, - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb-1", Id: "id-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_ERROR}, - }}}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - w, err := client.Watch(context.Background(), "default", "sb-1", WatchOptions{StopOnTerminal: true}) - require.NoError(t, err) - defer w.Stop() - - // Should receive the Provisioning event - ev1 := <-w.ResultChan() - assert.Equal(t, EventAdded, ev1.Type) - assert.Equal(t, SandboxProvisioning, ev1.Object.Status.Phase) - - // Should receive the Error event (terminal) - ev2 := <-w.ResultChan() - assert.Equal(t, EventModified, ev2.Type) - assert.Equal(t, SandboxError, ev2.Object.Status.Phase) - - // Channel should close automatically after terminal event (stream is still open) - select { - case _, ok := <-w.ResultChan(): - assert.False(t, ok, "channel should close after terminal Error event") - case <-time.After(time.Second): - t.Fatal("timed out waiting for channel close after terminal Error event") - } -} - -func TestSandboxWatch_StopOnTerminal_False_DoesNotClose(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["sb-1"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "id-1", Name: "sb-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - mock.watchKeepOpen = make(chan struct{}) - defer close(mock.watchKeepOpen) - mock.watchEvents = []*pb.SandboxStreamEvent{ - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb-1", Id: "id-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - }}}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - w, err := client.Watch(context.Background(), "default", "sb-1") - require.NoError(t, err) - defer w.Stop() - - ev := <-w.ResultChan() - assert.Equal(t, EventAdded, ev.Type) - assert.Equal(t, SandboxReady, ev.Object.Status.Phase) - - // Channel must NOT close: stream is still open and StopOnTerminal=false - select { - case <-w.ResultChan(): - t.Fatal("channel should stay open when StopOnTerminal is false") - case <-time.After(100 * time.Millisecond): - } -} - -func TestSandboxWatch_DeletedEvent(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["sb-1"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "id-1", Name: "sb-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - } - mock.watchEvents = []*pb.SandboxStreamEvent{ - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb-1", Id: "id-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_PROVISIONING}, - }}}, - {Payload: &pb.SandboxStreamEvent_Sandbox{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Name: "sb-1", Id: "id-1"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_DELETING}, - }}}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - w, err := client.Watch(context.Background(), "default", "sb-1") - require.NoError(t, err) - defer w.Stop() - - ev1 := <-w.ResultChan() - assert.Equal(t, EventAdded, ev1.Type) - - ev2 := <-w.ResultChan() - assert.Equal(t, EventDeleted, ev2.Type) - assert.Equal(t, SandboxDeleting, ev2.Object.Status.Phase) -} - -// --- T027: GetLogs tests --- - -func TestSandboxGetLogs(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["log-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "sb-id-123", Name: "log-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - mock.getLogsResp = &pb.GetSandboxLogsResponse{ - Logs: []*pb.SandboxLogLine{ - {EventTime: timestamppb.New(time.UnixMilli(1700000000000)), Level: "INFO", Target: "gateway", Message: "connected", Source: "gateway"}, - {EventTime: timestamppb.New(time.UnixMilli(1700000001000)), Level: "DEBUG", Target: "sandbox", Message: "init done", Source: "sandbox"}, - }, - BufferTotal: 42, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.GetLogs(context.Background(), "default", "log-sb") - - require.NoError(t, err) - require.NotNil(t, result) - assert.Len(t, result.Lines, 2) - assert.Equal(t, uint32(42), result.BufferTotal) - assert.Equal(t, "INFO", result.Lines[0].Level) - assert.Equal(t, "connected", result.Lines[0].Message) - assert.Equal(t, "gateway", result.Lines[0].Source) - assert.Equal(t, "DEBUG", result.Lines[1].Level) - assert.Equal(t, "init done", result.Lines[1].Message) - - // Verify name→id resolution: the proto request should contain the sandbox ID - mock.mu.Lock() - assert.Equal(t, "log-sb", mock.getLogsRequest.GetSandbox()) - mock.mu.Unlock() -} - -func TestSandboxGetLogs_WithOptions(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["opts-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "sb-id-opts", Name: "opts-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - mock.getLogsResp = &pb.GetSandboxLogsResponse{ - Logs: []*pb.SandboxLogLine{{EventTime: timestamppb.New(time.UnixMilli(1700000000000)), Level: "WARN", Message: "high cpu"}}, - BufferTotal: 100, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - since := time.Date(2023, 11, 14, 0, 0, 0, 0, time.UTC) - result, err := client.GetLogs(context.Background(), "default", "opts-sb", - WithLogLines(50), - WithLogSince(since), - WithLogSources("gateway", "sandbox"), - WithLogMinLevel("WARN"), - ) - - require.NoError(t, err) - require.NotNil(t, result) - assert.Len(t, result.Lines, 1) - assert.Equal(t, "WARN", result.Lines[0].Level) - - // Verify all options were passed to the proto request - mock.mu.Lock() - req := mock.getLogsRequest - mock.mu.Unlock() - assert.Equal(t, "opts-sb", req.GetSandbox()) - assert.Equal(t, uint32(50), req.GetLines()) - assert.Equal(t, since, req.GetSinceTime().AsTime()) - assert.Equal(t, []string{"gateway", "sandbox"}, req.GetSources()) - assert.Equal(t, "WARN", req.GetMinLevel()) -} - -func TestSandboxGetLogs_SandboxNotFound(t *testing.T) { - mock := newMockSandboxServer() - // No sandbox registered — Get will return NotFound - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.GetLogs(context.Background(), "default", "nonexistent") - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestSandboxGetLogs_RPCError(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["rpc-err-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "sb-id-rpc", Name: "rpc-err-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - mock.getLogsErr = status.Error(codes.Unavailable, "log service down") - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - _, err := client.GetLogs(context.Background(), "default", "rpc-err-sb") - - require.Error(t, err) - assert.True(t, IsUnavailable(err)) -} - -func TestSandboxGetLogs_EmptyResult(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["empty-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "sb-id-empty", Name: "empty-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - mock.getLogsResp = &pb.GetSandboxLogsResponse{ - BufferTotal: 0, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - result, err := client.GetLogs(context.Background(), "default", "empty-sb") - - require.NoError(t, err) - require.NotNil(t, result) - assert.Empty(t, result.Lines) - assert.Equal(t, uint32(0), result.BufferTotal) -} - -func TestSandboxGetLogs_SinceZeroNotSent(t *testing.T) { - mock := newMockSandboxServer() - mock.sandboxes["zero-sb"] = &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "sb-id-zero", Name: "zero-sb"}, - Status: &pb.SandboxStatus{Phase: pb.SandboxPhase_SANDBOX_PHASE_READY}, - } - client, cleanup := setupSandboxTest(t, mock) - defer cleanup() - - // Call without WithLogSince — SinceTime should be unset. - _, err := client.GetLogs(context.Background(), "default", "zero-sb") - - require.NoError(t, err) - mock.mu.Lock() - assert.Nil(t, mock.getLogsRequest.GetSinceTime()) - mock.mu.Unlock() -} diff --git a/sdk/go/openshell/v1/sandbox_template.go b/sdk/go/openshell/v1/sandbox_template.go deleted file mode 100644 index 52cb4f2784..0000000000 --- a/sdk/go/openshell/v1/sandbox_template.go +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// SandboxWorkloadTemplate is a reusable workspace-scoped sandbox template resource. -type SandboxWorkloadTemplate = types.SandboxWorkloadTemplate - -// SandboxWorkloadTemplateSpec holds reusable sandbox template settings. -type SandboxWorkloadTemplateSpec = types.SandboxWorkloadTemplateSpec - -// SandboxWorkloadConfig defines the portable workload for a reusable template. -type SandboxWorkloadConfig = types.SandboxWorkloadConfig - -// SandboxResources defines portable sandbox resource requirements. -type SandboxResources = types.SandboxResources - -// SandboxGPURequirements defines template GPU requirements. -type SandboxGPURequirements = types.SandboxGPURequirements - -// SandboxServiceLevel describes desired operational characteristics. -type SandboxServiceLevel = types.SandboxServiceLevel - -// SandboxStartup describes desired startup characteristics. -type SandboxStartup = types.SandboxStartup - -// SandboxWorkloadTemplateProvenance identifies the reusable template revision used to create a sandbox. -type SandboxWorkloadTemplateProvenance = types.SandboxWorkloadTemplateProvenance - -// SandboxTemplateInterface defines CRUD operations on reusable sandbox templates. -// -// The resource type is named SandboxWorkloadTemplate in the v1 Go SDK so it does -// not collide with the legacy inline SandboxTemplate field on SandboxSpec. -type SandboxTemplateInterface interface { - Create(ctx context.Context, workspace string, template *SandboxWorkloadTemplate) (*SandboxWorkloadTemplate, error) - Get(ctx context.Context, workspace, name string) (*SandboxWorkloadTemplate, error) - List(workspace string, opts ...ListOptions) (*Pager[*SandboxWorkloadTemplate], error) - ListAll(ctx context.Context, workspace string, opts ...ListOptions) ([]*SandboxWorkloadTemplate, error) - Delete(ctx context.Context, workspace, name string, opts ...DeleteOptions) (*DeletionResult, error) -} diff --git a/sdk/go/openshell/v1/sandbox_template_client.go b/sdk/go/openshell/v1/sandbox_template_client.go deleted file mode 100644 index bcf48185e9..0000000000 --- a/sdk/go/openshell/v1/sandbox_template_client.go +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" -) - -type sandboxTemplateClient struct { - client pb.OpenShellClient -} - -var _ SandboxTemplateInterface = (*sandboxTemplateClient)(nil) - -func newSandboxTemplateClient(conn grpc.ClientConnInterface) *sandboxTemplateClient { - return &sandboxTemplateClient{client: pb.NewOpenShellClient(conn)} -} - -func (s *sandboxTemplateClient) Create(ctx context.Context, workspace string, template *SandboxWorkloadTemplate) (*SandboxWorkloadTemplate, error) { - if template == nil { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "template must not be nil"} - } - protoTemplate, err := converter.SandboxWorkloadTemplateToProtoChecked(template) - if err != nil { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: err.Error()} - } - resp, err := s.client.CreateSandboxTemplate(ctx, &pb.CreateSandboxTemplateRequest{ - Template: protoTemplate, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.SandboxWorkloadTemplateFromProto(resp.GetTemplate()), nil -} - -func (s *sandboxTemplateClient) Get(ctx context.Context, workspace, name string) (*SandboxWorkloadTemplate, error) { - resp, err := s.client.GetSandboxTemplate(ctx, &pb.GetSandboxTemplateRequest{ - Name: name, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.SandboxWorkloadTemplateFromProto(resp.GetTemplate()), nil -} - -func (s *sandboxTemplateClient) List(workspace string, opts ...ListOptions) (*Pager[*SandboxWorkloadTemplate], error) { - pageSize, err := listPageSize(opts) - if err != nil { - return nil, err - } - var pageToken, labelSelector string - var allWorkspaces bool - if len(opts) > 0 { - pageToken = opts[0].PageToken - labelSelector = opts[0].LabelSelector - allWorkspaces = opts[0].AllWorkspaces - } - workspaceScope := namedWorkspaceScope(workspace) - if allWorkspaces { - workspaceScope = allWorkspacesScope() - } - return newPager(pageToken, func(ctx context.Context, pageToken string) (*Page[*SandboxWorkloadTemplate], error) { - req := &pb.ListSandboxTemplatesRequest{ - WorkspaceScope: workspaceScope, PageSize: pageSize, PageToken: pageToken, - LabelSelector: labelSelector, - } - resp, err := s.client.ListSandboxTemplates(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - templates := make([]*SandboxWorkloadTemplate, 0, len(resp.GetTemplates())) - for _, protoTemplate := range resp.GetTemplates() { - templates = append(templates, converter.SandboxWorkloadTemplateFromProto(protoTemplate)) - } - return &Page[*SandboxWorkloadTemplate]{Items: templates, NextPageToken: resp.GetNextPageToken()}, nil - }), nil -} - -func (s *sandboxTemplateClient) ListAll(ctx context.Context, workspace string, opts ...ListOptions) ([]*SandboxWorkloadTemplate, error) { - pager, err := s.List(workspace, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -func (s *sandboxTemplateClient) Delete(ctx context.Context, workspace, name string, opts ...DeleteOptions) (*DeletionResult, error) { - resp, err := s.client.DeleteSandboxTemplate(ctx, &pb.DeleteSandboxTemplateRequest{ - AllowMissing: allowMissing(opts), - Name: name, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return &DeletionResult{Outcome: DeletionOutcome(resp.GetOutcome())}, nil -} diff --git a/sdk/go/openshell/v1/sandbox_template_client_test.go b/sdk/go/openshell/v1/sandbox_template_client_test.go deleted file mode 100644 index 8d745344e2..0000000000 --- a/sdk/go/openshell/v1/sandbox_template_client_test.go +++ /dev/null @@ -1,270 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "net" - "sync" - "testing" - "time" - - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/durationpb" -) - -type mockSandboxTemplateServer struct { - pb.UnimplementedOpenShellServer - mu sync.Mutex - - templates map[string]*pb.SandboxWorkloadTemplate - - createRequest *pb.CreateSandboxTemplateRequest - getRequest *pb.GetSandboxTemplateRequest - listRequest *pb.ListSandboxTemplatesRequest - deleteRequest *pb.DeleteSandboxTemplateRequest - - createErr error - getErr error - listErr error - deleteErr error -} - -func newMockSandboxTemplateServer() *mockSandboxTemplateServer { - return &mockSandboxTemplateServer{ - templates: make(map[string]*pb.SandboxWorkloadTemplate), - } -} - -func (s *mockSandboxTemplateServer) CreateSandboxTemplate(_ context.Context, req *pb.CreateSandboxTemplateRequest) (*pb.SandboxTemplateResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.createRequest = proto.Clone(req).(*pb.CreateSandboxTemplateRequest) - if s.createErr != nil { - return nil, s.createErr - } - template := proto.Clone(req.GetTemplate()).(*pb.SandboxWorkloadTemplate) - if template.Metadata == nil { - template.Metadata = &dm.ObjectMeta{} - } - template.Metadata.Workspace = req.GetWorkspaceScope().GetWorkspace() - template.Metadata.ResourceVersion = 1 - s.templates[template.Metadata.GetName()] = template - return &pb.SandboxTemplateResponse{Template: proto.Clone(template).(*pb.SandboxWorkloadTemplate)}, nil -} - -func (s *mockSandboxTemplateServer) GetSandboxTemplate(_ context.Context, req *pb.GetSandboxTemplateRequest) (*pb.SandboxTemplateResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.getRequest = proto.Clone(req).(*pb.GetSandboxTemplateRequest) - if s.getErr != nil { - return nil, s.getErr - } - template, ok := s.templates[req.GetName()] - if !ok { - return nil, status.Errorf(codes.NotFound, "template %q not found", req.GetName()) - } - return &pb.SandboxTemplateResponse{Template: proto.Clone(template).(*pb.SandboxWorkloadTemplate)}, nil -} - -func (s *mockSandboxTemplateServer) ListSandboxTemplates(_ context.Context, req *pb.ListSandboxTemplatesRequest) (*pb.ListSandboxTemplatesResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.listRequest = proto.Clone(req).(*pb.ListSandboxTemplatesRequest) - if s.listErr != nil { - return nil, s.listErr - } - templates := make([]*pb.SandboxWorkloadTemplate, 0, len(s.templates)) - for _, template := range s.templates { - templates = append(templates, proto.Clone(template).(*pb.SandboxWorkloadTemplate)) - } - return &pb.ListSandboxTemplatesResponse{Templates: templates}, nil -} - -func (s *mockSandboxTemplateServer) DeleteSandboxTemplate(_ context.Context, req *pb.DeleteSandboxTemplateRequest) (*pb.DeleteSandboxTemplateResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.deleteRequest = proto.Clone(req).(*pb.DeleteSandboxTemplateRequest) - if s.deleteErr != nil { - return nil, s.deleteErr - } - delete(s.templates, req.GetName()) - return &pb.DeleteSandboxTemplateResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_COMPLETED}, nil -} - -func setupSandboxTemplateTest(t *testing.T, mock *mockSandboxTemplateServer) (*sandboxTemplateClient, func()) { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - - return newSandboxTemplateClient(conn), func() { - _ = conn.Close() - srv.Stop() - } -} - -func TestSandboxTemplateCreate(t *testing.T) { - mock := newMockSandboxTemplateServer() - client, cleanup := setupSandboxTemplateTest(t, mock) - defer cleanup() - gpuCount := uint32(1) - - template, err := client.Create(context.Background(), "default", &SandboxWorkloadTemplate{ - Name: "gpu-kata", - Labels: map[string]string{ - "team": "platform", - }, - Spec: SandboxWorkloadTemplateSpec{ - Workload: &SandboxWorkloadConfig{ - Image: "nvcr.io/nvidia/openshell:latest", - Environment: map[string]string{"NVIDIA_VISIBLE_DEVICES": "all"}, - Resources: &SandboxResources{ - CPU: "2", - Memory: "8Gi", - GPU: &SandboxGPURequirements{Count: &gpuCount}, - }, - }, - DriverConfig: map[string]any{ - "kubernetes": map[string]any{"runtimeClassName": "kata"}, - }, - DesiredServiceLevel: &SandboxServiceLevel{ - Startup: &SandboxStartup{ - ReadyWithin: 45 * time.Second, - MaxBurst: 2, - }, - }, - }, - }) - - require.NoError(t, err) - require.NotNil(t, template) - assert.Equal(t, "gpu-kata", template.Name) - assert.Equal(t, "default", template.Workspace) - assert.Equal(t, uint64(1), template.ResourceVersion) - mock.mu.Lock() - defer mock.mu.Unlock() - require.NotNil(t, mock.createRequest) - assert.Equal(t, "default", mock.createRequest.GetWorkspaceScope().GetWorkspace()) - assert.Equal(t, "gpu-kata", mock.createRequest.Template.Metadata.Name) - assert.Equal(t, "2", mock.createRequest.Template.Spec.Workload.Resources.Cpu) - assert.Equal(t, "8Gi", mock.createRequest.Template.Spec.Workload.Resources.Memory) - require.NotNil(t, mock.createRequest.Template.Spec.Workload.Resources.Gpu) - require.NotNil(t, mock.createRequest.Template.Spec.Workload.Resources.Gpu.Count) - assert.Equal(t, uint32(1), *mock.createRequest.Template.Spec.Workload.Resources.Gpu.Count) - assert.Equal(t, durationpb.New(45*time.Second), mock.createRequest.Template.Spec.DesiredServiceLevel.Startup.ReadyWithin) -} - -func TestSandboxTemplateCreate_RejectsNilTemplate(t *testing.T) { - mock := newMockSandboxTemplateServer() - client, cleanup := setupSandboxTemplateTest(t, mock) - defer cleanup() - - _, err := client.Create(context.Background(), "default", nil) - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestSandboxTemplateCreate_RejectsUnrepresentableDriverConfigBeforeRPC(t *testing.T) { - mock := newMockSandboxTemplateServer() - client, cleanup := setupSandboxTemplateTest(t, mock) - defer cleanup() - - _, err := client.Create(context.Background(), "default", &SandboxWorkloadTemplate{ - Name: "bad", - Spec: SandboxWorkloadTemplateSpec{ - DriverConfig: map[string]any{"invalid": make(chan int)}, - }, - }) - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) - mock.mu.Lock() - defer mock.mu.Unlock() - assert.Nil(t, mock.createRequest) -} - -func TestSandboxTemplateGetListDelete(t *testing.T) { - mock := newMockSandboxTemplateServer() - mock.templates["gpu-kata"] = &pb.SandboxWorkloadTemplate{ - Metadata: &dm.ObjectMeta{Name: "gpu-kata", Workspace: "default"}, - Spec: &pb.SandboxWorkloadTemplateSpec{ - Workload: &pb.SandboxWorkloadConfig{Image: "img:v1"}, - }, - } - client, cleanup := setupSandboxTemplateTest(t, mock) - defer cleanup() - - got, err := client.Get(context.Background(), "default", "gpu-kata") - require.NoError(t, err) - assert.Equal(t, "gpu-kata", got.Name) - assert.Equal(t, "img:v1", got.Spec.Workload.Image) - - list, err := client.ListAll(context.Background(), "default", ListOptions{ - PageSize: 10, - LabelSelector: "team=runtime", - }) - require.NoError(t, err) - require.Len(t, list, 1) - assert.Equal(t, "gpu-kata", list[0].Name) - - deleted, err := client.Delete(context.Background(), "default", "gpu-kata") - require.NoError(t, err) - assert.Equal(t, DeletionCompleted, deleted.Outcome) - - mock.mu.Lock() - defer mock.mu.Unlock() - require.NotNil(t, mock.getRequest) - assert.Equal(t, "default", mock.getRequest.GetWorkspaceScope().GetWorkspace()) - assert.Equal(t, "gpu-kata", mock.getRequest.Name) - require.NotNil(t, mock.listRequest) - assert.Equal(t, "default", mock.listRequest.GetWorkspaceScope().GetWorkspace()) - assert.Equal(t, int32(10), mock.listRequest.PageSize) - assert.Empty(t, mock.listRequest.PageToken) - assert.Equal(t, "team=runtime", mock.listRequest.LabelSelector) - require.NotNil(t, mock.deleteRequest) - assert.Equal(t, "default", mock.deleteRequest.GetWorkspaceScope().GetWorkspace()) - assert.Equal(t, "gpu-kata", mock.deleteRequest.Name) -} - -func TestSandboxTemplateList_RejectsNegativePagination(t *testing.T) { - mock := newMockSandboxTemplateServer() - client, cleanup := setupSandboxTemplateTest(t, mock) - defer cleanup() - - _, err := client.ListAll(context.Background(), "default", ListOptions{PageSize: -1}) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestSandboxTemplateList_EmptyReturnsNonNilSlice(t *testing.T) { - mock := newMockSandboxTemplateServer() - client, cleanup := setupSandboxTemplateTest(t, mock) - defer cleanup() - - templates, err := client.ListAll(context.Background(), "default") - - require.NoError(t, err) - assert.NotNil(t, templates) - assert.Empty(t, templates) -} diff --git a/sdk/go/openshell/v1/service.go b/sdk/go/openshell/v1/service.go deleted file mode 100644 index ccaf93ee4b..0000000000 --- a/sdk/go/openshell/v1/service.go +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// ServiceEndpoint represents an exposed HTTP service endpoint within a sandbox. -type ServiceEndpoint = types.ServiceEndpoint - -// ServiceExposure describes a loopback HTTP service to expose during sandbox creation. -type ServiceExposure = types.ServiceExposure - -// ServiceAuthorizationMode controls handling of an incoming application Authorization header. -type ServiceAuthorizationMode = types.ServiceAuthorizationMode - -const ( - // ServiceAuthorizationModeStrip removes Authorization before proxying to the service. - ServiceAuthorizationModeStrip = types.ServiceAuthorizationModeStrip - // ServiceAuthorizationModeBearerPassthrough forwards one valid bearer credential unchanged. - ServiceAuthorizationModeBearerPassthrough = types.ServiceAuthorizationModeBearerPassthrough -) - -// ExposeServiceOptions configures service exposure behavior. -type ExposeServiceOptions struct { - AuthorizationMode ServiceAuthorizationMode -} - -// ServiceInterface defines operations for managing sandbox service endpoints. -type ServiceInterface interface { - Expose(ctx context.Context, workspace, sandboxName, serviceName string, targetPort uint32, domain bool, opts ...ExposeServiceOptions) (*ServiceEndpoint, error) - Get(ctx context.Context, workspace, sandboxName, serviceName string) (*ServiceEndpoint, error) - List(workspace, sandboxName string, opts ...ListOptions) (*Pager[*ServiceEndpoint], error) - ListAll(ctx context.Context, workspace, sandboxName string, opts ...ListOptions) ([]*ServiceEndpoint, error) - Delete(ctx context.Context, workspace, sandboxName, serviceName string, opts ...DeleteOptions) (*DeletionResult, error) -} diff --git a/sdk/go/openshell/v1/service_client.go b/sdk/go/openshell/v1/service_client.go deleted file mode 100644 index 5606ebf2a0..0000000000 --- a/sdk/go/openshell/v1/service_client.go +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" -) - -type serviceClient struct { - client pb.OpenShellClient -} - -func newServiceClient(conn grpc.ClientConnInterface) *serviceClient { - return &serviceClient{client: pb.NewOpenShellClient(conn)} -} - -func (s *serviceClient) Expose(ctx context.Context, workspace, sandboxName, serviceName string, targetPort uint32, domain bool, opts ...ExposeServiceOptions) (*ServiceEndpoint, error) { - authorizationMode := ServiceAuthorizationModeStrip - if len(opts) > 0 && opts[0].AuthorizationMode != 0 { - authorizationMode = opts[0].AuthorizationMode - } - resp, err := s.client.ExposeService(ctx, &pb.ExposeServiceRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - Name: serviceName, - TargetPort: targetPort, - Domain: domain, - AuthorizationMode: pb.ServiceAuthorizationMode(authorizationMode), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.ServiceEndpointFromProto(resp), nil -} - -func (s *serviceClient) Get(ctx context.Context, workspace, sandboxName, serviceName string) (*ServiceEndpoint, error) { - resp, err := s.client.GetService(ctx, &pb.GetServiceRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - Name: serviceName, - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.ServiceEndpointFromProto(resp), nil -} - -func (s *serviceClient) List(workspace, sandboxName string, opts ...ListOptions) (*Pager[*ServiceEndpoint], error) { - pageSize, err := listPageSize(opts) - if err != nil { - return nil, err - } - var pageToken string - var allWorkspaces bool - if len(opts) > 0 { - pageToken = opts[0].PageToken - allWorkspaces = opts[0].AllWorkspaces - } - workspaceScope := namedWorkspaceScope(workspace) - if allWorkspaces { - workspaceScope = allWorkspacesScope() - } - return newPager(pageToken, func(ctx context.Context, pageToken string) (*Page[*ServiceEndpoint], error) { - req := &pb.ListServicesRequest{ - Sandbox: sandboxName, WorkspaceScope: workspaceScope, PageSize: pageSize, - PageToken: pageToken, - } - resp, err := s.client.ListServices(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - endpoints := make([]*ServiceEndpoint, 0, len(resp.GetServices())) - for _, svc := range resp.GetServices() { - endpoints = append(endpoints, converter.ServiceEndpointFromProto(svc)) - } - return &Page[*ServiceEndpoint]{Items: endpoints, NextPageToken: resp.GetNextPageToken()}, nil - }), nil -} - -func (s *serviceClient) ListAll(ctx context.Context, workspace, sandboxName string, opts ...ListOptions) ([]*ServiceEndpoint, error) { - pager, err := s.List(workspace, sandboxName, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -func (s *serviceClient) Delete(ctx context.Context, workspace, sandboxName, serviceName string, opts ...DeleteOptions) (*DeletionResult, error) { - resp, err := s.client.DeleteService(ctx, &pb.DeleteServiceRequest{ - AllowMissing: allowMissing(opts), - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - Name: serviceName, - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return &DeletionResult{Outcome: DeletionOutcome(resp.GetOutcome())}, nil -} diff --git a/sdk/go/openshell/v1/service_client_test.go b/sdk/go/openshell/v1/service_client_test.go deleted file mode 100644 index 3ba2e5e69a..0000000000 --- a/sdk/go/openshell/v1/service_client_test.go +++ /dev/null @@ -1,355 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "net" - "sync" - "testing" - - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" -) - -// --- Mock server for service endpoints --- - -type mockServiceServer struct { - pb.UnimplementedOpenShellServer - mu sync.Mutex - endpoints map[string]*pb.ServiceEndpointResponse // key: "sandbox/service" - exposeErr error - getErr error - listErr error - deleteErr error - lastList *pb.ListServicesRequest -} - -func newMockServiceServer() *mockServiceServer { - return &mockServiceServer{ - endpoints: make(map[string]*pb.ServiceEndpointResponse), - } -} - -func serviceKey(sandbox, service string) string { - return sandbox + "/" + service -} - -func (s *mockServiceServer) ExposeService(_ context.Context, req *pb.ExposeServiceRequest) (*pb.ServiceEndpointResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.exposeErr != nil { - return nil, s.exposeErr - } - - resp := &pb.ServiceEndpointResponse{ - Endpoint: &pb.ServiceEndpoint{ - Metadata: &dm.ObjectMeta{ - Id: "ep-" + req.GetName(), - }, - Sandbox: req.GetSandbox(), - Name: req.GetName(), - TargetPort: req.GetTargetPort(), - Domain: req.GetDomain(), - AuthorizationMode: req.GetAuthorizationMode(), - }, - } - if req.GetDomain() { - resp.Url = "https://" + req.GetName() + ".example.com" - } - - s.endpoints[serviceKey(req.GetSandbox(), req.GetName())] = resp - return resp, nil -} - -func (s *mockServiceServer) GetService(_ context.Context, req *pb.GetServiceRequest) (*pb.ServiceEndpointResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.getErr != nil { - return nil, s.getErr - } - - sandboxName := req.GetSandbox() - ep, ok := s.endpoints[serviceKey(sandboxName, req.GetName())] - if !ok { - return nil, status.Errorf(codes.NotFound, "service %q not found in sandbox %q", req.GetName(), sandboxName) - } - return ep, nil -} - -func (s *mockServiceServer) ListServices(_ context.Context, req *pb.ListServicesRequest) (*pb.ListServicesResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastList = req - if s.listErr != nil { - return nil, s.listErr - } - - var services []*pb.ServiceEndpointResponse - for key, ep := range s.endpoints { - sandboxName := req.GetSandbox() - prefix := sandboxName + "/" - if sandboxName == "" || (len(key) >= len(prefix) && key[:len(prefix)] == prefix) { - services = append(services, ep) - } - } - return &pb.ListServicesResponse{Services: services}, nil -} - -func (s *mockServiceServer) DeleteService(_ context.Context, req *pb.DeleteServiceRequest) (*pb.DeleteServiceResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.deleteErr != nil { - return nil, s.deleteErr - } - - sandboxName := req.GetSandbox() - key := serviceKey(sandboxName, req.GetName()) - _, ok := s.endpoints[key] - if !ok { - return nil, status.Errorf(codes.NotFound, "service %q not found in sandbox %q", req.GetName(), sandboxName) - } - delete(s.endpoints, key) - return &pb.DeleteServiceResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_COMPLETED}, nil -} - -// --- Test setup --- - -func setupServiceTest(t *testing.T, mock *mockServiceServer) (*serviceClient, func()) { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - - return newServiceClient(conn), func() { - _ = conn.Close() - srv.Stop() - } -} - -// --- Tests --- - -func TestServiceExpose(t *testing.T) { - mock := newMockServiceServer() - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - ep, err := client.Expose( - context.Background(), - "default", - "web-app", - "api", - 8080, - true, - ExposeServiceOptions{AuthorizationMode: ServiceAuthorizationModeBearerPassthrough}, - ) - - require.NoError(t, err) - require.NotNil(t, ep) - assert.Equal(t, "ep-api", ep.ID) - assert.Equal(t, "web-app", ep.Sandbox) - assert.Equal(t, "api", ep.Name) - assert.Equal(t, uint32(8080), ep.TargetPort) - assert.True(t, ep.Domain) - assert.Equal(t, ServiceAuthorizationModeBearerPassthrough, ep.AuthorizationMode) - assert.Equal(t, "https://api.example.com", ep.URL) -} - -func TestServiceExpose_NoDomain(t *testing.T) { - mock := newMockServiceServer() - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - ep, err := client.Expose(context.Background(), "default", "web-app", "api", 8080, false) - - require.NoError(t, err) - require.NotNil(t, ep) - assert.False(t, ep.Domain) - assert.Empty(t, ep.URL) -} - -func TestServiceExpose_Error(t *testing.T) { - mock := newMockServiceServer() - mock.exposeErr = status.Errorf(codes.NotFound, "sandbox not found") - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - ep, err := client.Expose(context.Background(), "default", "missing", "api", 8080, true) - - assert.Nil(t, ep) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestServiceGet(t *testing.T) { - mock := newMockServiceServer() - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - // First expose, then get - _, err := client.Expose(context.Background(), "default", "web-app", "api", 8080, true) - require.NoError(t, err) - - ep, err := client.Get(context.Background(), "default", "web-app", "api") - - require.NoError(t, err) - require.NotNil(t, ep) - assert.Equal(t, "api", ep.Name) - assert.Equal(t, "web-app", ep.Sandbox) -} - -func TestServiceGet_NotFound(t *testing.T) { - mock := newMockServiceServer() - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - ep, err := client.Get(context.Background(), "default", "web-app", "nonexistent") - - assert.Nil(t, ep) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestServiceGet_Error(t *testing.T) { - mock := newMockServiceServer() - mock.getErr = status.Errorf(codes.Internal, "internal error") - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - ep, err := client.Get(context.Background(), "default", "web-app", "api") - - assert.Nil(t, ep) - require.Error(t, err) -} - -func TestServiceList(t *testing.T) { - mock := newMockServiceServer() - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - // Expose two services - _, err := client.Expose(context.Background(), "default", "web-app", "api", 8080, true) - require.NoError(t, err) - _, err = client.Expose(context.Background(), "default", "web-app", "web", 3000, false) - require.NoError(t, err) - - endpoints, err := client.ListAll(context.Background(), "default", "web-app") - - require.NoError(t, err) - assert.Len(t, endpoints, 2) -} - -func TestServiceList_Empty(t *testing.T) { - mock := newMockServiceServer() - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - endpoints, err := client.ListAll(context.Background(), "default", "web-app") - - require.NoError(t, err) - assert.NotNil(t, endpoints) - assert.Empty(t, endpoints) -} - -func TestServiceList_WithOptions(t *testing.T) { - mock := newMockServiceServer() - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - _, err := client.Expose(context.Background(), "default", "web-app", "api", 8080, true) - require.NoError(t, err) - - endpoints, err := client.ListAll(context.Background(), "default", "web-app", ListOptions{PageSize: 10}) - - require.NoError(t, err) - assert.Len(t, endpoints, 1) -} - -func TestServiceListAll_SelectsAllWorkspaces(t *testing.T) { - mock := newMockServiceServer() - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - endpoints, err := client.ListAll(context.Background(), "", "", ListOptions{ - PageSize: 10, - AllWorkspaces: true, - }) - - require.NoError(t, err) - assert.Empty(t, endpoints) - require.NotNil(t, mock.lastList) - assert.Empty(t, mock.lastList.GetSandbox()) - assert.NotNil(t, mock.lastList.GetWorkspaceScope().GetAllWorkspaces()) -} - -func TestServiceList_Error(t *testing.T) { - mock := newMockServiceServer() - mock.listErr = status.Errorf(codes.Unavailable, "unavailable") - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - endpoints, err := client.ListAll(context.Background(), "default", "web-app") - - assert.Nil(t, endpoints) - require.Error(t, err) - assert.True(t, IsUnavailable(err)) -} - -func TestServiceDelete(t *testing.T) { - mock := newMockServiceServer() - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - // Expose then delete - _, err := client.Expose(context.Background(), "default", "web-app", "api", 8080, true) - require.NoError(t, err) - - _, err = client.Delete(context.Background(), "default", "web-app", "api") - - require.NoError(t, err) - - // Verify subsequent Get returns NotFound - ep, err := client.Get(context.Background(), "default", "web-app", "api") - assert.Nil(t, ep) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestServiceDelete_NotFound(t *testing.T) { - mock := newMockServiceServer() - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - _, err := client.Delete(context.Background(), "default", "web-app", "nonexistent") - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestServiceDelete_Error(t *testing.T) { - mock := newMockServiceServer() - mock.deleteErr = status.Errorf(codes.Internal, "internal error") - client, cleanup := setupServiceTest(t, mock) - defer cleanup() - - _, err := client.Delete(context.Background(), "default", "web-app", "api") - - require.Error(t, err) -} diff --git a/sdk/go/openshell/v1/ssh.go b/sdk/go/openshell/v1/ssh.go deleted file mode 100644 index 392b6c261e..0000000000 --- a/sdk/go/openshell/v1/ssh.go +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "io" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// SSHSession represents an SSH session created for a sandbox. -type SSHSession = types.SSHSession - -// tunnelConfig accumulates options for the Tunnel method. -type tunnelConfig struct { - serviceID string -} - -// TunnelOption configures an SSH tunnel opened via [SSHInterface.Tunnel]. -type TunnelOption func(*tunnelConfig) - -// WithTunnelServiceID sets an optional service identifier on the tunnel's -// init frame for audit and correlation purposes. -func WithTunnelServiceID(id string) TunnelOption { - return func(c *tunnelConfig) { - c.serviceID = id - } -} - -// SSHInterface defines operations for managing SSH sessions. -type SSHInterface interface { - CreateSession(ctx context.Context, workspace, sandboxName string) (*SSHSession, error) - RevokeSession(ctx context.Context, workspace, token string, opts ...DeleteOptions) (*DeletionResult, error) - Tunnel(ctx context.Context, workspace, sandboxName string, port uint32, opts ...TunnelOption) (io.ReadWriteCloser, error) -} diff --git a/sdk/go/openshell/v1/ssh_client.go b/sdk/go/openshell/v1/ssh_client.go deleted file mode 100644 index aa19db4f28..0000000000 --- a/sdk/go/openshell/v1/ssh_client.go +++ /dev/null @@ -1,157 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "fmt" - "io" - "sync" - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" -) - -const sshCleanupTimeout = 5 * time.Second - -type sshClient struct { - client pb.OpenShellClient - sandboxes SandboxInterface -} - -func newSSHClient(conn grpc.ClientConnInterface, sandboxes SandboxInterface) *sshClient { - return &sshClient{ - client: pb.NewOpenShellClient(conn), - sandboxes: sandboxes, - } -} - -func (s *sshClient) CreateSession(ctx context.Context, workspace, sandboxName string) (*SSHSession, error) { - resp, err := s.client.CreateSshSession(ctx, &pb.CreateSshSessionRequest{ - Sandbox: sandboxName, - WorkspaceScope: namedWorkspaceScope(workspace), - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.SSHSessionFromProto(resp), nil -} - -func (s *sshClient) RevokeSession(ctx context.Context, _, token string, opts ...DeleteOptions) (*DeletionResult, error) { - resp, err := s.client.RevokeSshSession(ctx, &pb.RevokeSshSessionRequest{ - AllowMissing: allowMissing(opts), - Token: token, - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return &DeletionResult{Outcome: DeletionOutcome(resp.GetOutcome())}, nil -} - -func (s *sshClient) Tunnel(ctx context.Context, workspace, sandboxName string, port uint32, opts ...TunnelOption) (io.ReadWriteCloser, error) { - if sandboxName == "" { - return nil, &StatusError{ - Code: ErrorInvalidArgument, - Message: "sandbox name must not be empty", - } - } - if port == 0 || port > 65535 { - return nil, &StatusError{ - Code: ErrorInvalidArgument, - Message: fmt.Sprintf("port must be in range 1-65535, got %d", port), - } - } - owner, err := sandboxOwner(ctx, s.sandboxes, workspace, sandboxName) - if err != nil { - return nil, err - } - - var cfg tunnelConfig - options.Apply(&cfg, opts) - - session, err := s.CreateSession(ctx, workspace, sandboxName) - if err != nil { - return nil, err - } - - revokeSession := true - defer func() { - if revokeSession { - s.revokeSessionForCleanup(workspace, session.Token) - } - }() - - initFrame := &pb.TcpForwardFrame{ - Payload: &pb.TcpForwardFrame_Init{ - Init: &pb.TcpForwardInit{ - Sandbox: sandboxName, - Workspace: workspace, - ServiceId: cfg.serviceID, - AuthorizationToken: session.Token, - Target: &pb.TcpForwardInit_Ssh{ - Ssh: &pb.SshRelayTarget{}, - }, - }, - }, - } - - streamCtx, cancel := context.WithCancel(ctx) - stream, err := openBidiRouted(streamCtx, owner, initFrame, s.client.ForwardTcp) //nolint:revive // proto-generated method name - if err != nil { - cancel() - return nil, converter.FromGRPCError(err) - } - - conn := &tcpForwardConn{ - stream: stream, - streamCtx: streamCtx, - cancel: cancel, - dataCh: make(chan []byte, 64), - done: make(chan struct{}), - } - go conn.readLoop() - - revokeSession = false - - t := &sshTunnel{ - tcpForwardConn: conn, - revokeFunc: func() { - s.revokeSessionForCleanup(workspace, session.Token) - }, - } - - // Auto-revoke the SSH session when the parent context is cancelled. - // The done channel closes after readLoop exits (stream fully drained), - // so Close() won't race with an active stream. - go func() { - <-conn.done - _ = t.Close() - }() - - return t, nil -} - -func (s *sshClient) revokeSessionForCleanup(workspace, token string) { - ctx, cancel := context.WithTimeout(context.Background(), sshCleanupTimeout) - defer cancel() - _, _ = s.RevokeSession(ctx, workspace, token, DeleteOptions{AllowMissing: true}) -} - -type sshTunnel struct { - *tcpForwardConn - revokeFunc func() - closeOnce sync.Once - closeErr error -} - -func (t *sshTunnel) Close() error { - t.closeOnce.Do(func() { - t.closeErr = t.tcpForwardConn.Close() - t.revokeFunc() - }) - return t.closeErr -} diff --git a/sdk/go/openshell/v1/ssh_client_test.go b/sdk/go/openshell/v1/ssh_client_test.go deleted file mode 100644 index c5209b5d2f..0000000000 --- a/sdk/go/openshell/v1/ssh_client_test.go +++ /dev/null @@ -1,635 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "fmt" - "net" - "sync" - "testing" - "time" - - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" - "google.golang.org/protobuf/types/known/timestamppb" -) - -// --- Mock server for SSH sessions --- - -type mockSSHServer struct { - pb.UnimplementedOpenShellServer - mu sync.Mutex - sessions map[string]*pb.CreateSshSessionResponse // key: sandbox ID - tokens map[string]bool // track active tokens - revokeCount int // total revocation attempts - revokeHasDeadline bool - createErr error - revokeErr error - forwardErr error - nextToken string // override token for testing - lastInit *pb.TcpForwardInit -} - -func newMockSSHServer() *mockSSHServer { - return &mockSSHServer{ - sessions: make(map[string]*pb.CreateSshSessionResponse), - tokens: make(map[string]bool), - } -} - -func (s *mockSSHServer) CreateSshSession(_ context.Context, req *pb.CreateSshSessionRequest) (*pb.CreateSshSessionResponse, error) { //nolint:revive // proto-generated method name - s.mu.Lock() - defer s.mu.Unlock() - if s.createErr != nil { - return nil, s.createErr - } - - sandboxName := req.GetSandbox() - token := "tok-" + sandboxName - if s.nextToken != "" { - token = s.nextToken - } - - resp := &pb.CreateSshSessionResponse{ - SandboxId: "id-" + sandboxName, - Token: token, - GatewayHost: "gw.example.com", - GatewayPort: 2222, - GatewayScheme: "https", - HostKeyFingerprint: "SHA256:abc123", - ExpirationTime: timestamppb.New(time.UnixMilli(1700000000000)), - } - s.sessions[sandboxName] = resp - s.tokens[token] = true - return resp, nil -} - -func (s *mockSSHServer) RevokeSshSession(ctx context.Context, req *pb.RevokeSshSessionRequest) (*pb.RevokeSshSessionResponse, error) { //nolint:revive // proto-generated method name - s.mu.Lock() - defer s.mu.Unlock() - s.revokeCount++ - _, s.revokeHasDeadline = ctx.Deadline() - if s.revokeErr != nil { - return nil, s.revokeErr - } - - token := req.GetToken() - _, exists := s.tokens[token] - if exists { - s.tokens[token] = false - return &pb.RevokeSshSessionResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_COMPLETED}, nil - } - if !req.AllowMissing { - return nil, status.Error(codes.NotFound, "ssh session not found") - } - return &pb.RevokeSshSessionResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_ALREADY_ABSENT}, nil -} - -func (s *mockSSHServer) ForwardTcp(stream grpc.BidiStreamingServer[pb.TcpForwardFrame, pb.TcpForwardFrame]) error { //nolint:revive // proto-generated method name - s.mu.Lock() - earlyErr := s.forwardErr - s.mu.Unlock() - if earlyErr != nil { - return earlyErr - } - - frame, err := stream.Recv() - if err != nil { - return err - } - init := frame.GetInit() - if init == nil { - return status.Errorf(codes.InvalidArgument, "first frame must be init") - } - - s.mu.Lock() - s.lastInit = init - s.mu.Unlock() - - for { - frame, err = stream.Recv() - if err != nil { - return err - } - data := frame.GetData() - if data == nil { - continue - } - if err := stream.Send(&pb.TcpForwardFrame{ - Payload: &pb.TcpForwardFrame_Data{Data: data}, - }); err != nil { - return err - } - } -} - -// --- Mock sandbox resolver --- - -type mockSandboxResolver struct { - sandboxes map[string]*Sandbox - err error -} - -func (m *mockSandboxResolver) Create(_ context.Context, _, _ string, _ *SandboxSpec, _ ...CreateOption) (*Sandbox, error) { - return nil, nil -} - -func (m *mockSandboxResolver) Get(_ context.Context, _, name string) (*Sandbox, error) { - if m.err != nil { - return nil, m.err - } - sb, ok := m.sandboxes[name] - if !ok { - return nil, &StatusError{Code: ErrorNotFound, Message: "sandbox not found: " + name} - } - return sb, nil -} - -func (m *mockSandboxResolver) List(_ string, _ ...ListOptions) (*Pager[*Sandbox], error) { - return NewPager("", func(_ context.Context, _ string) (*Page[*Sandbox], error) { - return &Page[*Sandbox]{Items: make([]*Sandbox, 0)}, nil - }), nil -} -func (m *mockSandboxResolver) ListAll(_ context.Context, _ string, _ ...ListOptions) ([]*Sandbox, error) { - return nil, nil -} -func (m *mockSandboxResolver) Delete(_ context.Context, _, _ string, _ ...DeleteOptions) (*DeletionResult, error) { - return &DeletionResult{Outcome: DeletionCompleted}, nil -} -func (m *mockSandboxResolver) AttachProvider(_ context.Context, _, _, _ string, _ uint64) (*AttachProviderResult, error) { - return nil, nil -} -func (m *mockSandboxResolver) DetachProvider(_ context.Context, _, _, _ string, _ uint64) (*DetachProviderResult, error) { - return nil, nil -} -func (m *mockSandboxResolver) ListProviders(_, _ string, _ ...ListOptions) (*Pager[*Provider], error) { - return nil, nil -} -func (m *mockSandboxResolver) ListAllProviders(_ context.Context, _, _ string, _ ...ListOptions) ([]*Provider, error) { - return nil, nil -} -func (m *mockSandboxResolver) WaitReady(_ context.Context, _, _ string, _ ...WaitOptions) (*Sandbox, error) { - return nil, nil -} -func (m *mockSandboxResolver) Watch(_ context.Context, _, _ string, _ ...WatchOptions) (WatchInterface[*Sandbox], error) { - return nil, nil -} -func (m *mockSandboxResolver) GetLogs(_ context.Context, _, _ string, _ ...LogOption) (*LogResult, error) { - return nil, nil -} -func (m *mockSandboxResolver) Stop(_ context.Context, _, _ string) (*Sandbox, error) { - return nil, nil -} -func (m *mockSandboxResolver) Start(_ context.Context, _, _ string) (*Sandbox, error) { - return nil, nil -} -func (m *mockSandboxResolver) WaitStopped(_ context.Context, _, _ string, _ ...WaitOptions) (*Sandbox, error) { - return nil, nil -} - -// --- Test setup --- - -func setupSSHTest(t *testing.T, mock *mockSSHServer) (*sshClient, func()) { - t.Helper() - return setupSSHTestWithSandboxes(t, mock, nil) -} - -func setupSSHTestWithSandboxes(t *testing.T, mock *mockSSHServer, sandboxes SandboxInterface) (*sshClient, func()) { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - - return newSSHClient(conn, sandboxes), func() { - _ = conn.Close() - srv.Stop() - } -} - -// --- Tests --- - -func TestSSHCreateSession(t *testing.T) { - mock := newMockSSHServer() - client, cleanup := setupSSHTest(t, mock) - defer cleanup() - - session, err := client.CreateSession(context.Background(), "default", "my-sandbox") - - require.NoError(t, err) - require.NotNil(t, session) - assert.Equal(t, "id-my-sandbox", session.SandboxID) - assert.Equal(t, "tok-my-sandbox", session.Token) - assert.Equal(t, "gw.example.com", session.GatewayHost) - assert.Equal(t, uint32(2222), session.GatewayPort) - assert.Equal(t, "https", session.GatewayScheme) - assert.Equal(t, "SHA256:abc123", session.HostKeyFingerprint) - assert.Equal(t, int64(1700000000000), session.ExpiresAtMs) -} - -func TestSSHCreateSession_Error(t *testing.T) { - mock := newMockSSHServer() - mock.createErr = status.Errorf(codes.NotFound, "sandbox not found") - client, cleanup := setupSSHTest(t, mock) - defer cleanup() - - session, err := client.CreateSession(context.Background(), "default", "missing") - - assert.Nil(t, session) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestSSHRevokeSession(t *testing.T) { - mock := newMockSSHServer() - client, cleanup := setupSSHTest(t, mock) - defer cleanup() - - // Create a session first. - session, err := client.CreateSession(context.Background(), "default", "my-sandbox") - require.NoError(t, err) - - // Revoke it — should return true. - revoked, err := client.RevokeSession(context.Background(), "default", session.Token) - - require.NoError(t, err) - assert.Equal(t, DeletionCompleted, revoked.Outcome) -} - -func TestSSHRevokeSession_AlreadyRevoked(t *testing.T) { - mock := newMockSSHServer() - client, cleanup := setupSSHTest(t, mock) - defer cleanup() - - // Create and revoke. - session, err := client.CreateSession(context.Background(), "default", "my-sandbox") - require.NoError(t, err) - _, err = client.RevokeSession(context.Background(), "default", session.Token) - require.NoError(t, err) - - // Revocation is already effective; the retained session is still completed. - revoked, err := client.RevokeSession(context.Background(), "default", session.Token) - - require.NoError(t, err) - assert.Equal(t, DeletionCompleted, revoked.Outcome) -} - -func TestSSHRevokeSession_Error(t *testing.T) { - mock := newMockSSHServer() - mock.revokeErr = status.Errorf(codes.Internal, "internal error") - client, cleanup := setupSSHTest(t, mock) - defer cleanup() - - revoked, err := client.RevokeSession(context.Background(), "default", "some-token") - - assert.Nil(t, revoked) - require.Error(t, err) - var se *StatusError - require.ErrorAs(t, err, &se) - assert.Equal(t, ErrorInternal, se.Code) -} - -// --- Tunnel tests (T012) --- - -func defaultSandboxResolver() *mockSandboxResolver { - return &mockSandboxResolver{ - sandboxes: map[string]*Sandbox{ - "my-sandbox": {ID: "sb-123", Name: "my-sandbox"}, - }, - } -} - -func TestSSHTunnel_Success(t *testing.T) { - mock := newMockSSHServer() - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - rwc, err := client.Tunnel(context.Background(), "default", "my-sandbox", 22) - require.NoError(t, err) - require.NotNil(t, rwc) - defer func() { _ = rwc.Close() }() - - // Round-trip to verify the stream works. - _, err = rwc.Write([]byte("hello")) - require.NoError(t, err) - - buf := make([]byte, 64) - n, err := rwc.Read(buf) - require.NoError(t, err) - assert.Equal(t, "hello", string(buf[:n])) - - // Verify init frame sent to server. - mock.mu.Lock() - init := mock.lastInit - mock.mu.Unlock() - - require.NotNil(t, init) - assert.Equal(t, "my-sandbox", init.GetSandbox()) - assert.NotEmpty(t, init.GetAuthorizationToken()) - assert.NotNil(t, init.GetSsh(), "target should be SshRelayTarget") -} - -func TestSSHTunnel_WithServiceID(t *testing.T) { - mock := newMockSSHServer() - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - rwc, err := client.Tunnel(context.Background(), "default", "my-sandbox", 22, WithTunnelServiceID("audit-svc")) - require.NoError(t, err) - require.NotNil(t, rwc) - defer func() { _ = rwc.Close() }() - - _, err = rwc.Write([]byte("ping")) - require.NoError(t, err) - buf := make([]byte, 64) - _, err = rwc.Read(buf) - require.NoError(t, err) - - mock.mu.Lock() - init := mock.lastInit - mock.mu.Unlock() - - require.NotNil(t, init) - assert.Equal(t, "audit-svc", init.GetServiceId()) -} - -func TestSSHTunnel_InvalidPort(t *testing.T) { - mock := newMockSSHServer() - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - tests := []struct { - name string - port uint32 - }{ - {"port zero", 0}, - {"port too high", 65536}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rwc, err := client.Tunnel(context.Background(), "default", "my-sandbox", tt.port) - assert.Nil(t, rwc) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) - }) - } -} - -func TestSSHTunnel_EmptySandboxName(t *testing.T) { - mock := newMockSSHServer() - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - rwc, err := client.Tunnel(context.Background(), "default", "", 22) - assert.Nil(t, rwc) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestSSHTunnel_SandboxNotFound(t *testing.T) { - mock := newMockSSHServer() - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - rwc, err := client.Tunnel(context.Background(), "default", "nonexistent", 22) - assert.Nil(t, rwc) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestSSHTunnel_SessionRevokedOnForwardFailure(t *testing.T) { - mock := newMockSSHServer() - mock.forwardErr = status.Errorf(codes.Internal, "forward failed") - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - rwc, err := client.Tunnel(context.Background(), "default", "my-sandbox", 22) - - if err != nil { - assert.Nil(t, rwc) - } else { - require.NotNil(t, rwc) - buf := make([]byte, 64) - _, err = rwc.Read(buf) - assert.Error(t, err) - _ = rwc.Close() - } - - // Session should have been revoked since the forward failed. - mock.mu.Lock() - tokenRevoked := false - for _, active := range mock.tokens { - if !active { - tokenRevoked = true - break - } - } - mock.mu.Unlock() - assert.True(t, tokenRevoked, "session token should be revoked after forward failure") -} - -func TestSSHTunnel_SessionRevokedOnClose(t *testing.T) { - mock := newMockSSHServer() - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - rwc, err := client.Tunnel(context.Background(), "default", "my-sandbox", 22) - require.NoError(t, err) - - // Close the tunnel, which should revoke the session. - err = rwc.Close() - require.NoError(t, err) - - mock.mu.Lock() - tokenRevoked := false - for _, active := range mock.tokens { - if !active { - tokenRevoked = true - break - } - } - mock.mu.Unlock() - assert.True(t, tokenRevoked, "session token should be revoked after tunnel close") - assert.True(t, mock.revokeHasDeadline, "cleanup revocation must have a deadline") -} - -func TestSSHTunnel_DoubleClose(t *testing.T) { - mock := newMockSSHServer() - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - rwc, err := client.Tunnel(context.Background(), "default", "my-sandbox", 22) - require.NoError(t, err) - - err = rwc.Close() - require.NoError(t, err) - - // Second close should not panic or return a different error. - err = rwc.Close() - assert.NoError(t, err) -} - -func TestSSHTunnel_ContextCancellation(t *testing.T) { - mock := newMockSSHServer() - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - ctx, cancel := context.WithCancel(context.Background()) - rwc, err := client.Tunnel(ctx, "default", "my-sandbox", 22) - require.NoError(t, err) - require.NotNil(t, rwc) - - cancel() - - buf := make([]byte, 64) - _, err = rwc.Read(buf) - assert.Error(t, err) - - _, err = rwc.Write([]byte("should fail")) - assert.Error(t, err) - - _ = rwc.Close() -} - -func TestSSHTunnel_TokenNotExposed(t *testing.T) { - mock := newMockSSHServer() - mock.nextToken = "secret-tunnel-token-xyz" - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - rwc, err := client.Tunnel(context.Background(), "default", "my-sandbox", 22) - require.NoError(t, err) - require.NotNil(t, rwc) - defer func() { _ = rwc.Close() }() - - repr := fmt.Sprintf("%v", rwc) - assert.NotContains(t, repr, "secret-tunnel-token-xyz", - "token must not leak through the returned value's string representation") -} - -func TestSSHTunnel_ContextCancelRevokesSession(t *testing.T) { - mock := newMockSSHServer() - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - ctx, cancel := context.WithCancel(context.Background()) - rwc, err := client.Tunnel(ctx, "default", "my-sandbox", 22) - require.NoError(t, err) - require.NotNil(t, rwc) - - cancel() - - require.Eventually(t, func() bool { - mock.mu.Lock() - defer mock.mu.Unlock() - for _, active := range mock.tokens { - if !active { - return true - } - } - return false - }, 5*time.Second, 10*time.Millisecond, "session token should be revoked after context cancel") -} - -func TestSSHTunnel_ContextCancelThenClose(t *testing.T) { - mock := newMockSSHServer() - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - ctx, cancel := context.WithCancel(context.Background()) - rwc, err := client.Tunnel(ctx, "default", "my-sandbox", 22) - require.NoError(t, err) - require.NotNil(t, rwc) - - cancel() - - // Wait for the cleanup goroutine to complete its revocation. - require.Eventually(t, func() bool { - mock.mu.Lock() - defer mock.mu.Unlock() - for _, active := range mock.tokens { - if !active { - return true - } - } - return false - }, 5*time.Second, 10*time.Millisecond) - - // Explicit Close() after the cleanup goroutine already ran. - err = rwc.Close() - assert.NoError(t, err) - - mock.mu.Lock() - count := mock.revokeCount - mock.mu.Unlock() - assert.Equal(t, 1, count, "exactly one revocation should occur (closeOnce idempotency)") -} - -func TestSSHTunnel_CloseBeforeContextCancel(t *testing.T) { - mock := newMockSSHServer() - resolver := defaultSandboxResolver() - client, cleanup := setupSSHTestWithSandboxes(t, mock, resolver) - defer cleanup() - - ctx, cancel := context.WithCancel(context.Background()) - rwc, err := client.Tunnel(ctx, "default", "my-sandbox", 22) - require.NoError(t, err) - require.NotNil(t, rwc) - - // Explicit Close() first (revokes the session). - err = rwc.Close() - require.NoError(t, err) - - // Cancel context after Close() already completed. - cancel() - - // Verify the cleanup goroutine does not trigger a second revocation. - require.Never(t, func() bool { - mock.mu.Lock() - defer mock.mu.Unlock() - return mock.revokeCount > 1 - }, 200*time.Millisecond, 10*time.Millisecond, "cleanup goroutine should not revoke again") - - mock.mu.Lock() - count := mock.revokeCount - tokenRevoked := false - for _, active := range mock.tokens { - if !active { - tokenRevoked = true - break - } - } - mock.mu.Unlock() - - assert.True(t, tokenRevoked, "session should be revoked") - assert.Equal(t, 1, count, "exactly one revocation should occur") -} diff --git a/sdk/go/openshell/v1/tcp.go b/sdk/go/openshell/v1/tcp.go deleted file mode 100644 index a8fcae133d..0000000000 --- a/sdk/go/openshell/v1/tcp.go +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "io" - "net" -) - -// forwardConfig accumulates options for the Forward method. -type forwardConfig struct { - serviceID string -} - -// ForwardOption configures a TCP forward opened via [TCPInterface.Forward]. -type ForwardOption func(*forwardConfig) - -// WithForwardServiceID sets an optional service identifier on the forward's -// init frame for audit and correlation purposes. -func WithForwardServiceID(id string) ForwardOption { - return func(c *forwardConfig) { - c.serviceID = id - } -} - -// listenConfig accumulates options for the Listen method. -type listenConfig struct { - bindAddress string - useSSHTunnel bool - serviceID string -} - -// ListenOption configures a local listener opened via [TCPInterface.Listen]. -type ListenOption func(*listenConfig) - -// ForwardListener is the lifecycle handle for a local TCP forward. The SDK -// owns accepting and bridging local connections; callers dial Addr and call -// Close when the forwarding endpoint is no longer needed. -type ForwardListener interface { - Addr() net.Addr - Close() error -} - -// WithBindAddress overrides the default local bind address ("127.0.0.1"). -// Pass "0.0.0.0" to accept connections from any interface. -func WithBindAddress(addr string) ListenOption { - return func(c *listenConfig) { - c.bindAddress = addr - } -} - -// WithSSHTunnel routes each accepted connection through an SSH tunnel -// ([SSHInterface.Tunnel]) instead of the default TCP forward -// ([TCPInterface.Forward]). -func WithSSHTunnel() ListenOption { - return func(c *listenConfig) { - c.useSSHTunnel = true - } -} - -// WithListenServiceID sets an optional service identifier on each tunneled -// connection's init frame for audit and correlation purposes. -func WithListenServiceID(id string) ListenOption { - return func(c *listenConfig) { - c.serviceID = id - } -} - -// TCPInterface defines operations for TCP port forwarding to sandboxes. -// Methods accept a sandbox name and resolve it to an ID internally. -type TCPInterface interface { - Forward(ctx context.Context, workspace, sandboxName string, port uint32, opts ...ForwardOption) (io.ReadWriteCloser, error) - Listen(ctx context.Context, workspace, sandboxName string, remotePort uint32, localPort uint32, opts ...ListenOption) (ForwardListener, error) -} diff --git a/sdk/go/openshell/v1/tcp_client.go b/sdk/go/openshell/v1/tcp_client.go deleted file mode 100644 index a3585478db..0000000000 --- a/sdk/go/openshell/v1/tcp_client.go +++ /dev/null @@ -1,349 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "fmt" - "io" - "net" - "strconv" - "sync" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" -) - -type tcpClient struct { - client pb.OpenShellClient - sandboxes SandboxInterface - ssh SSHInterface -} - -func newTCPClient(conn grpc.ClientConnInterface, sandboxes SandboxInterface, ssh SSHInterface) *tcpClient { - return &tcpClient{client: pb.NewOpenShellClient(conn), sandboxes: sandboxes, ssh: ssh} -} - -func (t *tcpClient) Forward(ctx context.Context, workspace, sandboxName string, port uint32, opts ...ForwardOption) (io.ReadWriteCloser, error) { - if sandboxName == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - if port == 0 || port > 65535 { - return nil, &StatusError{ - Code: ErrorInvalidArgument, - Message: fmt.Sprintf("port must be in range 1-65535, got %d", port), - } - } - owner, err := sandboxOwner(ctx, t.sandboxes, workspace, sandboxName) - if err != nil { - return nil, err - } - - var cfg forwardConfig - options.Apply(&cfg, opts) - - initFrame := &pb.TcpForwardFrame{ - Payload: &pb.TcpForwardFrame_Init{ - Init: &pb.TcpForwardInit{ - Sandbox: sandboxName, - Workspace: workspace, - ServiceId: cfg.serviceID, - Target: &pb.TcpForwardInit_Tcp{ - Tcp: &pb.TcpRelayTarget{ - Host: "127.0.0.1", - Port: port, - }, - }, - }, - }, - } - - streamCtx, cancel := context.WithCancel(ctx) - stream, err := openBidiRouted(streamCtx, owner, initFrame, t.client.ForwardTcp) //nolint:revive // proto-generated method name - if err != nil { - cancel() - return nil, converter.FromGRPCError(err) - } - - conn := &tcpForwardConn{ - stream: stream, - streamCtx: streamCtx, - cancel: cancel, - dataCh: make(chan []byte, 64), - done: make(chan struct{}), - } - go conn.readLoop() - return conn, nil -} - -func (t *tcpClient) Listen(ctx context.Context, workspace, sandboxName string, remotePort uint32, localPort uint32, opts ...ListenOption) (ForwardListener, error) { - if sandboxName == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "sandbox name must not be empty"} - } - if remotePort == 0 || remotePort > 65535 { - return nil, &StatusError{ - Code: ErrorInvalidArgument, - Message: fmt.Sprintf("port must be in range 1-65535, got %d", remotePort), - } - } - if localPort > 65535 { - return nil, &StatusError{ - Code: ErrorInvalidArgument, - Message: fmt.Sprintf("local port must be in range 0-65535, got %d", localPort), - } - } - - cfg := listenConfig{bindAddress: "127.0.0.1"} - options.Apply(&cfg, opts) - - if cfg.useSSHTunnel && t.ssh == nil { - return nil, &StatusError{ - Code: ErrorInvalidArgument, - Message: "WithSSHTunnel requires an SSH client, but none is available", - } - } - - addr := net.JoinHostPort(cfg.bindAddress, strconv.FormatUint(uint64(localPort), 10)) - inner, err := net.Listen("tcp", addr) - if err != nil { - return nil, fmt.Errorf("listen on %s: %w", addr, err) - } - - listenCtx, cancel := context.WithCancel(ctx) - tl := &tunnelListener{ - inner: inner, - ctx: listenCtx, - cancel: cancel, - tcp: t, - ssh: t.ssh, - workspace: workspace, - sandboxName: sandboxName, - remotePort: remotePort, - cfg: cfg, - } - - // Context-watcher: if the parent context is cancelled, close the listener. - go func() { - <-listenCtx.Done() - _ = tl.Close() - }() - - // The listener is a forwarding lifecycle handle: it owns acceptance and - // bridging. Callers only dial Addr() and close the handle when finished. - tl.wg.Add(1) - go tl.acceptLoop() - - return tl, nil -} - -// tunnelListener implements net.Listener. It accepts local TCP connections -// and bridges each one to a sandbox port via Forward (or Tunnel in SSH mode). -type tunnelListener struct { - inner net.Listener - ctx context.Context - cancel context.CancelFunc - tcp *tcpClient - ssh SSHInterface - workspace string - sandboxName string - remotePort uint32 - cfg listenConfig - wg sync.WaitGroup - mu sync.Mutex - closing bool - closeOnce sync.Once - closeErr error -} - -// acceptLoop accepts local connections and hands each one to its own -// goroutine, so a slow or hung tunnel setup never delays other clients. -func (tl *tunnelListener) acceptLoop() { - defer tl.wg.Done() - for { - conn, err := tl.inner.Accept() - if err != nil { - return - } - - tl.mu.Lock() - if tl.closing { - tl.mu.Unlock() - _ = conn.Close() - return - } - tl.wg.Add(1) - tl.mu.Unlock() - go tl.serve(conn) - } -} - -// openTunnel establishes a tunnel to the sandbox port, over SSH or ForwardTcp. -func (tl *tunnelListener) openTunnel() (io.ReadWriteCloser, error) { - if tl.cfg.useSSHTunnel && tl.ssh != nil { - var opts []TunnelOption - if tl.cfg.serviceID != "" { - opts = append(opts, WithTunnelServiceID(tl.cfg.serviceID)) - } - return tl.ssh.Tunnel(tl.ctx, tl.workspace, tl.sandboxName, tl.remotePort, opts...) - } - var opts []ForwardOption - if tl.cfg.serviceID != "" { - opts = append(opts, WithForwardServiceID(tl.cfg.serviceID)) - } - return tl.tcp.Forward(tl.ctx, tl.workspace, tl.sandboxName, tl.remotePort, opts...) -} - -// serve sets up the tunnel for one local connection and bridges the two. -// It runs in its own goroutine and decrements the WaitGroup on exit. -func (tl *tunnelListener) serve(local net.Conn) { - defer tl.wg.Done() - tunnel, err := tl.openTunnel() - if err != nil { - _ = local.Close() - return - } - if tl.ctx.Err() != nil { - _ = local.Close() - _ = tunnel.Close() - return - } - tl.bridge(local, tunnel) -} - -// bridge copies data bidirectionally between the local connection and the -// tunnel until either side closes, then closes both. -func (tl *tunnelListener) bridge(local net.Conn, tunnel io.ReadWriteCloser) { - defer func() { _ = local.Close() }() - defer func() { _ = tunnel.Close() }() - - done := make(chan struct{}, 2) - - // Local → tunnel - go func() { - _, _ = io.Copy(tunnel, local) - done <- struct{}{} - }() - - // Tunnel → local - go func() { - _, _ = io.Copy(local, tunnel) - done <- struct{}{} - }() - - <-done - _ = local.Close() - _ = tunnel.Close() - <-done -} - -// Close stops the listener from accepting new connections, cancels all -// active tunnels, and blocks until all bridge goroutines finish. -func (tl *tunnelListener) Close() error { - tl.closeOnce.Do(func() { - tl.mu.Lock() - tl.closing = true - tl.mu.Unlock() - tl.closeErr = tl.inner.Close() - tl.cancel() - tl.wg.Wait() - }) - return tl.closeErr -} - -// Addr returns the listener's network address (the bound local address). -func (tl *tunnelListener) Addr() net.Addr { - return tl.inner.Addr() -} - -// tcpForwardConn wraps a bidirectional TcpForwardFrame stream into an -// io.ReadWriteCloser. A background goroutine owns the Recv loop and routes -// data frames to dataCh. Read and Write may be called from different -// goroutines, but multiple concurrent Read callers are not supported. -type tcpForwardConn struct { - stream grpc.BidiStreamingClient[pb.TcpForwardFrame, pb.TcpForwardFrame] - streamCtx context.Context - cancel context.CancelFunc - sendMu sync.Mutex - dataCh chan []byte - done chan struct{} - errOnce sync.Once - err error - buf []byte -} - -func (c *tcpForwardConn) setErr(err error) { - c.errOnce.Do(func() { c.err = err }) -} - -func (c *tcpForwardConn) readLoop() { - defer close(c.dataCh) - defer close(c.done) - for { - frame, err := c.stream.Recv() - if err != nil { - if err != io.EOF { - c.setErr(converter.FromGRPCError(err)) - } - return - } - data := frame.GetData() - if data == nil { - continue - } - dataCopy := make([]byte, len(data)) - copy(dataCopy, data) - select { - case c.dataCh <- dataCopy: - case <-c.streamCtx.Done(): - return - } - } -} - -func (c *tcpForwardConn) Read(p []byte) (int, error) { - if len(p) == 0 { - return 0, nil - } - if len(c.buf) > 0 { - n := copy(p, c.buf) - c.buf = c.buf[n:] - return n, nil - } - - data, ok := <-c.dataCh - if !ok { - if c.err != nil { - return 0, c.err - } - return 0, io.EOF - } - n := copy(p, data) - if n < len(data) { - c.buf = append(c.buf, data[n:]...) - } - return n, nil -} - -func (c *tcpForwardConn) Write(p []byte) (int, error) { - c.sendMu.Lock() - defer c.sendMu.Unlock() - err := c.stream.Send(&pb.TcpForwardFrame{ - Payload: &pb.TcpForwardFrame_Data{Data: p}, - }) - if err != nil { - return 0, converter.FromGRPCError(err) - } - return len(p), nil -} - -func (c *tcpForwardConn) Close() error { - c.sendMu.Lock() - err := c.stream.CloseSend() - c.sendMu.Unlock() - c.cancel() - <-c.done - return err -} diff --git a/sdk/go/openshell/v1/tcp_client_test.go b/sdk/go/openshell/v1/tcp_client_test.go deleted file mode 100644 index adef1d8205..0000000000 --- a/sdk/go/openshell/v1/tcp_client_test.go +++ /dev/null @@ -1,1306 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "errors" - "fmt" - "io" - "net" - "sync" - "sync/atomic" - "testing" - "time" - - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" -) - -// --- Mock server for TCP forwarding --- - -// mockTCPServer implements the ForwardTcp bidi stream. It records the init -// frame and echoes every data frame back to the client. -type mockTCPServer struct { - pb.UnimplementedOpenShellServer - mu sync.Mutex - lastInit *pb.TcpForwardInit - err error // if non-nil, return this error immediately on stream open -} - -func newMockTCPServer() *mockTCPServer { - return &mockTCPServer{} -} - -func (s *mockTCPServer) ForwardTcp(stream grpc.BidiStreamingServer[pb.TcpForwardFrame, pb.TcpForwardFrame]) error { //nolint:revive // proto-generated method name - s.mu.Lock() - earlyErr := s.err - s.mu.Unlock() - if earlyErr != nil { - return earlyErr - } - - // First frame must be init. - frame, err := stream.Recv() - if err != nil { - return err - } - init := frame.GetInit() - if init == nil { - return status.Errorf(codes.InvalidArgument, "first frame must be init") - } - - s.mu.Lock() - s.lastInit = init - s.mu.Unlock() - - // Echo loop: every data frame is sent back verbatim. - for { - frame, err = stream.Recv() - if err != nil { - return err - } - data := frame.GetData() - if data == nil { - continue - } - if err := stream.Send(&pb.TcpForwardFrame{ - Payload: &pb.TcpForwardFrame_Data{Data: data}, - }); err != nil { - return err - } - } -} - -// --- Test setup --- - -func setupTCPTest(t *testing.T, mock *mockTCPServer) (*tcpClient, func()) { - t.Helper() - return setupTCPTestWithResolver(t, mock, &stubSandboxResolver{}) -} - -func setupTCPTestWithResolver(t *testing.T, mock *mockTCPServer, resolver SandboxInterface) (*tcpClient, func()) { - t.Helper() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - - return newTCPClient(conn, resolver, nil), func() { - _ = conn.Close() - srv.Stop() - } -} - -// --- Tests --- - -func TestTCPForward_InitFrame(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - rwc, err := client.Forward(context.Background(), "default", "my-sandbox", 8080) - require.NoError(t, err) - require.NotNil(t, rwc) - defer func() { _ = rwc.Close() }() - - // Write something to trigger the init frame to be sent (init is sent - // on Forward, before any Write — but we need a brief moment for the - // server to process it). - _, err = rwc.Write([]byte("ping")) - require.NoError(t, err) - - // Read back the echo. - buf := make([]byte, 64) - n, err := rwc.Read(buf) - require.NoError(t, err) - assert.Equal(t, "ping", string(buf[:n])) - - // Verify the init frame the server received. - mock.mu.Lock() - init := mock.lastInit - mock.mu.Unlock() - - require.NotNil(t, init) - assert.Equal(t, "my-sandbox", init.GetSandbox()) - assert.Empty(t, init.GetServiceId(), "service_id should be empty per FR-007a") - assert.Empty(t, init.GetAuthorizationToken()) - - tcp := init.GetTcp() - require.NotNil(t, tcp, "target should be TcpRelayTarget") - assert.Equal(t, "127.0.0.1", tcp.GetHost()) - assert.Equal(t, uint32(8080), tcp.GetPort()) -} - -func TestTCPForward_ReadWrite(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - rwc, err := client.Forward(context.Background(), "default", "test-sandbox", 3000) - require.NoError(t, err) - defer func() { _ = rwc.Close() }() - - // Write data and read the echo back. - payload := []byte("hello, sandbox!") - _, err = rwc.Write(payload) - require.NoError(t, err) - - buf := make([]byte, 64) - n, err := rwc.Read(buf) - require.NoError(t, err) - assert.Equal(t, payload, buf[:n]) - - // Second round-trip. - _, err = rwc.Write([]byte("round2")) - require.NoError(t, err) - - n, err = rwc.Read(buf) - require.NoError(t, err) - assert.Equal(t, "round2", string(buf[:n])) -} - -func TestTCPForward_Close(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - rwc, err := client.Forward(context.Background(), "default", "my-sandbox", 5432) - require.NoError(t, err) - - err = rwc.Close() - require.NoError(t, err) - - // Subsequent writes should fail. - _, err = rwc.Write([]byte("should fail")) - assert.Error(t, err) - - // Subsequent reads should also fail. - buf := make([]byte, 64) - _, err = rwc.Read(buf) - assert.Error(t, err) -} - -func TestTCPForward_PartialRead(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - rwc, err := client.Forward(context.Background(), "default", "my-sandbox", 8080) - require.NoError(t, err) - defer func() { _ = rwc.Close() }() - - // Write a payload larger than the read buffer. - payload := []byte("abcdefghijklmnopqrstuvwxyz") - _, err = rwc.Write(payload) - require.NoError(t, err) - - // Read with a small buffer — should get partial data and buffer the rest. - var collected []byte - buf := make([]byte, 10) - for len(collected) < len(payload) { - n, readErr := rwc.Read(buf) - require.NoError(t, readErr) - collected = append(collected, buf[:n]...) - } - assert.Equal(t, payload, collected) -} - -func TestTCPForward_ConcurrentReadWrite(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - rwc, err := client.Forward(context.Background(), "default", "my-sandbox", 8080) - require.NoError(t, err) - defer func() { _ = rwc.Close() }() - - const iterations = 50 - var wg sync.WaitGroup - errCh := make(chan error, 2) - wg.Add(2) - - go func() { - defer wg.Done() - for range iterations { - _, writeErr := rwc.Write([]byte("ping")) - if writeErr != nil { - errCh <- writeErr - return - } - } - }() - - go func() { - defer wg.Done() - buf := make([]byte, 64) - for range iterations { - _, readErr := rwc.Read(buf) - if readErr != nil { - errCh <- readErr - return - } - } - }() - - wg.Wait() - close(errCh) - for err := range errCh { - t.Fatalf("concurrent goroutine failed: %v", err) - } -} - -func TestTCPForward_PortValidation(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - tests := []struct { - name string - port uint32 - }{ - {"port zero", 0}, - {"port too high", 65536}, - {"port way too high", 100000}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rwc, err := client.Forward(context.Background(), "default", "my-sandbox", tt.port) - assert.Nil(t, rwc) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err), "expected InvalidArgument, got: %v", err) - }) - } - - // Valid boundary ports should not get client-side rejection. - for _, port := range []uint32{1, 65535} { - rwc, err := client.Forward(context.Background(), "default", "my-sandbox", port) - require.NoError(t, err, "port %d should be valid", port) - require.NotNil(t, rwc) - _ = rwc.Close() - } -} - -func TestTCPForward_ContextCancellation(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - ctx, cancel := context.WithCancel(context.Background()) - rwc, err := client.Forward(ctx, "default", "my-sandbox", 8080) - require.NoError(t, err) - require.NotNil(t, rwc) - - // Cancel the context. - cancel() - - // Reads should return an error (context cancelled propagates through the gRPC stream). - buf := make([]byte, 64) - _, err = rwc.Read(buf) - assert.Error(t, err) - - // Writes should also fail after context cancellation. - _, err = rwc.Write([]byte("should fail")) - assert.Error(t, err) -} - -func TestTCPForward_WithServiceID(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - rwc, err := client.Forward(context.Background(), "default", "my-sandbox", 8080, WithForwardServiceID("audit-svc")) - require.NoError(t, err) - require.NotNil(t, rwc) - defer func() { _ = rwc.Close() }() - - // Trigger a round-trip so the server has processed the init frame. - _, err = rwc.Write([]byte("ping")) - require.NoError(t, err) - buf := make([]byte, 64) - _, err = rwc.Read(buf) - require.NoError(t, err) - - mock.mu.Lock() - init := mock.lastInit - mock.mu.Unlock() - - require.NotNil(t, init) - assert.Equal(t, "audit-svc", init.GetServiceId()) - assert.Equal(t, "my-sandbox", init.GetSandbox()) -} - -func TestTCPForward_WithoutOptions_BackwardCompat(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - rwc, err := client.Forward(context.Background(), "default", "my-sandbox", 8080) - require.NoError(t, err) - require.NotNil(t, rwc) - defer func() { _ = rwc.Close() }() - - _, err = rwc.Write([]byte("ping")) - require.NoError(t, err) - buf := make([]byte, 64) - _, err = rwc.Read(buf) - require.NoError(t, err) - - mock.mu.Lock() - init := mock.lastInit - mock.mu.Unlock() - - require.NotNil(t, init) - assert.Empty(t, init.GetServiceId(), "service_id should be empty when no option provided") -} - -func TestTCPForward_ServerError(t *testing.T) { - mock := newMockTCPServer() - mock.err = status.Errorf(codes.Unavailable, "server unavailable") - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - rwc, err := client.Forward(context.Background(), "default", "my-sandbox", 8080) - - // The stream opens successfully (gRPC bidi streams don't fail on open), - // but the first write or read should surface the server error. - if err != nil { - // When Send(initFrame) races with the server returning the error, - // the client may get the server status or a transport-level error. - assert.Nil(t, rwc) - require.Error(t, err) - return - } - - // If stream opened, the error surfaces on Read (the server returns it - // immediately, which closes the recv side). - require.NotNil(t, rwc) - defer func() { _ = rwc.Close() }() - - buf := make([]byte, 64) - _, err = rwc.Read(buf) - assert.Error(t, err) -} - -// --- Name-to-ID resolution tests --- - -func TestTCPForward_UsesName(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - rwc, err := client.Forward(context.Background(), "default", "my-sandbox", 8080) - require.NoError(t, err) - require.NotNil(t, rwc) - defer func() { _ = rwc.Close() }() - - // Trigger a round-trip so the server has processed the init frame. - _, err = rwc.Write([]byte("ping")) - require.NoError(t, err) - buf := make([]byte, 64) - _, err = rwc.Read(buf) - require.NoError(t, err) - - mock.mu.Lock() - init := mock.lastInit - mock.mu.Unlock() - - require.NotNil(t, init) - // stubSandboxResolver returns ID "sb-" — verify the proto has the resolved ID, not the name - assert.Equal(t, "my-sandbox", init.GetSandbox()) -} - -func TestTCPForward_ResolutionError(t *testing.T) { - mock := newMockTCPServer() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - defer func() { - _ = conn.Close() - srv.Stop() - }() - - resolver := &stubSandboxResolver{ - getErr: &StatusError{Code: ErrorNotFound, Message: "sandbox not found"}, - } - client := newTCPClient(conn, resolver, nil) - - rwc, err := client.Forward(context.Background(), "default", "nonexistent", 8080) - assert.Nil(t, rwc) - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestTCPForward_EmptySandboxName(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - rwc, err := client.Forward(context.Background(), "default", "", 8080) - assert.Nil(t, rwc) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -// --- Listen tests --- - -func TestTCPListen_ReturnsValidListener(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - ln, err := client.Listen(context.Background(), "default", "my-sandbox", 8080, 0) - require.NoError(t, err) - require.NotNil(t, ln) - defer func() { _ = ln.Close() }() - - // Addr should return a non-nil TCP address with a non-zero port. - addr := ln.Addr() - require.NotNil(t, addr) - tcpAddr, ok := addr.(*net.TCPAddr) - require.True(t, ok, "expected *net.TCPAddr, got %T", addr) - assert.NotZero(t, tcpAddr.Port, "OS-assigned port should be non-zero") -} - -func TestTCPListen_ConcurrentConnections(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - ln, err := client.Listen(context.Background(), "default", "my-sandbox", 8080, 0) - require.NoError(t, err) - require.NotNil(t, ln) - defer func() { _ = ln.Close() }() - - const numConns = 10 - var wg sync.WaitGroup - errCh := make(chan error, numConns) - - // Dial numConns goroutines, each independently writes and reads. - for i := range numConns { - wg.Add(1) - go func(idx int) { - defer wg.Done() - - conn, dialErr := net.Dial("tcp", ln.Addr().String()) - if dialErr != nil { - errCh <- fmt.Errorf("dial %d: %w", idx, dialErr) - return - } - defer func() { _ = conn.Close() }() - - payload := []byte(fmt.Sprintf("msg-%d", idx)) - _, writeErr := conn.Write(payload) - if writeErr != nil { - errCh <- fmt.Errorf("write %d: %w", idx, writeErr) - return - } - - buf := make([]byte, 256) - n, readErr := conn.Read(buf) - if readErr != nil { - errCh <- fmt.Errorf("read %d: %w", idx, readErr) - return - } - - if string(buf[:n]) != string(payload) { - errCh <- fmt.Errorf("conn %d: expected %q, got %q", idx, payload, buf[:n]) - } - }(i) - } - - wg.Wait() - close(errCh) - for err := range errCh { - t.Errorf("concurrent connection error: %v", err) - } -} - -func TestTCPListen_EphemeralPort(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - // localPort=0 → OS assigns an ephemeral port. - ln, err := client.Listen(context.Background(), "default", "my-sandbox", 8080, 0) - require.NoError(t, err) - require.NotNil(t, ln) - defer func() { _ = ln.Close() }() - - // Addr() should expose the assigned port. - tcpAddr, ok := ln.Addr().(*net.TCPAddr) - require.True(t, ok) - assert.NotZero(t, tcpAddr.Port, "OS-assigned port should be non-zero") - - // Verify a connection through the ephemeral port actually works. - conn, err := net.Dial("tcp", ln.Addr().String()) - require.NoError(t, err) - defer func() { _ = conn.Close() }() - - payload := []byte("ephemeral-test") - _, err = conn.Write(payload) - require.NoError(t, err) - - buf := make([]byte, 256) - n, err := conn.Read(buf) - require.NoError(t, err) - assert.Equal(t, payload, buf[:n]) -} - -func TestTCPListen_EmptySandboxName(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - ln, err := client.Listen(context.Background(), "default", "", 8080, 0) - assert.Nil(t, ln) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestTCPListen_BidirectionalDataFlow(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - ln, err := client.Listen(context.Background(), "default", "my-sandbox", 8080, 0) - require.NoError(t, err) - require.NotNil(t, ln) - defer func() { _ = ln.Close() }() - - // Connect to the listener's local address. - conn, err := net.Dial("tcp", ln.Addr().String()) - require.NoError(t, err) - defer func() { _ = conn.Close() }() - - // Write data through the local connection → tunnel → mock echo → back. - payload := []byte("hello through the tunnel") - _, err = conn.Write(payload) - require.NoError(t, err) - - // Read the echoed data back. - buf := make([]byte, 256) - n, err := conn.Read(buf) - require.NoError(t, err) - assert.Equal(t, payload, buf[:n]) - - // Second round-trip to confirm bidirectionality. - payload2 := []byte("round two") - _, err = conn.Write(payload2) - require.NoError(t, err) - - n, err = conn.Read(buf) - require.NoError(t, err) - assert.Equal(t, payload2, buf[:n]) -} - -func TestTCPListen_InvalidRemotePort(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - tests := []struct { - name string - port uint32 - }{ - {"port zero", 0}, - {"port too high", 65536}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ln, err := client.Listen(context.Background(), "default", "my-sandbox", tt.port, 0) - assert.Nil(t, ln) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err), "expected InvalidArgument, got: %v", err) - }) - } -} - -// --- Graceful shutdown tests --- - -func TestTCPListen_CloseTerminatesConnections(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - ln, err := client.Listen(context.Background(), "default", "my-sandbox", 8080, 0) - require.NoError(t, err) - - const numConns = 3 - conns := make([]net.Conn, numConns) - - // Establish 3 connections. - for i := range numConns { - conns[i], err = net.Dial("tcp", ln.Addr().String()) - require.NoError(t, err) - - // Verify data flows before shutdown. - _, err = conns[i].Write([]byte("pre-close")) - require.NoError(t, err) - buf := make([]byte, 256) - _, err = conns[i].Read(buf) - require.NoError(t, err) - } - - // Close the listener. Per SC-003, this should complete within 5 seconds. - closeDone := make(chan error, 1) - go func() { - closeDone <- ln.Close() - }() - - select { - case closeErr := <-closeDone: - assert.NoError(t, closeErr) - case <-time.After(5 * time.Second): - t.Fatal("Close did not complete within 5 seconds") - } - - // All connections should now return errors on read. - for i, conn := range conns { - buf := make([]byte, 64) - _, readErr := conn.Read(buf) - assert.Error(t, readErr, "connection %d should be closed after listener.Close()", i) - _ = conn.Close() - } -} - -func TestTCPListen_ContextCancellation(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - ctx, cancel := context.WithCancel(context.Background()) - ln, err := client.Listen(ctx, "default", "my-sandbox", 8080, 0) - require.NoError(t, err) - require.NotNil(t, ln) - - conn, err := net.Dial("tcp", ln.Addr().String()) - require.NoError(t, err) - - // Verify data flows before cancellation. - _, err = conn.Write([]byte("before-cancel")) - require.NoError(t, err) - buf := make([]byte, 256) - _, err = conn.Read(buf) - require.NoError(t, err) - - // Cancel the context — should trigger listener close. - cancel() - - // The connection should eventually fail. - // Give the context-watcher goroutine a moment to close the listener. - time.Sleep(50 * time.Millisecond) - - _, err = conn.Write([]byte("after-cancel")) - if err == nil { - // Write may succeed if buffered, but Read should fail. - buf = make([]byte, 64) - _, err = conn.Read(buf) - } - assert.Error(t, err, "connection should fail after context cancellation") - _ = conn.Close() -} - -func TestTCPListen_CloseIsIdempotent(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - ln, err := client.Listen(context.Background(), "default", "my-sandbox", 8080, 0) - require.NoError(t, err) - - // Close the listener immediately. - err = ln.Close() - require.NoError(t, err) - - assert.NoError(t, ln.Close()) -} - -// --- Custom bind address tests --- - -func TestTCPListen_WithBindAddress(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - // Verify WithBindAddress is accepted and the listener binds to the - // specified address. We use 127.0.0.1 explicitly since it is the only - // loopback address guaranteed on all platforms (macOS does not enable - // 127.0.0.2+ by default). The default-case assertion below confirms - // that omitting the option also produces 127.0.0.1. - ln, err := client.Listen( - context.Background(), "default", "my-sandbox", 8080, 0, - WithBindAddress("127.0.0.1"), - ) - require.NoError(t, err) - defer func() { _ = ln.Close() }() - - tcpAddr, ok := ln.Addr().(*net.TCPAddr) - require.True(t, ok, "expected *net.TCPAddr") - assert.Equal(t, "127.0.0.1", tcpAddr.IP.String(), - "listener should bind to the address specified by WithBindAddress") - - // Also verify that without WithBindAddress the default is 127.0.0.1. - lnDefault, err := client.Listen( - context.Background(), "default", "my-sandbox", 8080, 0, - ) - require.NoError(t, err) - defer func() { _ = lnDefault.Close() }() - - defaultAddr, ok := lnDefault.Addr().(*net.TCPAddr) - require.True(t, ok, "expected *net.TCPAddr") - assert.Equal(t, "127.0.0.1", defaultAddr.IP.String(), - "default bind address should be 127.0.0.1") -} - -// --- SSH tunnel transport tests --- - -// mockSSHClient implements SSHInterface for testing the SSH tunnel path. -type mockSSHClient struct { - mu sync.Mutex - tunnelCalls int -} - -func (m *mockSSHClient) CreateSession(_ context.Context, _, _ string) (*SSHSession, error) { - return nil, fmt.Errorf("not implemented in mock") -} - -func (m *mockSSHClient) RevokeSession(_ context.Context, _, _ string, _ ...DeleteOptions) (*DeletionResult, error) { - return nil, fmt.Errorf("not implemented in mock") -} - -// Tunnel returns a pipe that echoes data back, and increments the call counter. -func (m *mockSSHClient) Tunnel(_ context.Context, _, _ string, _ uint32, _ ...TunnelOption) (io.ReadWriteCloser, error) { - m.mu.Lock() - m.tunnelCalls++ - m.mu.Unlock() - - // Create a pipe-based echo tunnel: read from one end, write back to the other. - clientReader, serverWriter := io.Pipe() - serverReader, clientWriter := io.Pipe() - - // Echo goroutine: copy everything from server reader to server writer. - go func() { - buf := make([]byte, 4096) - for { - n, err := serverReader.Read(buf) - if err != nil { - _ = serverWriter.Close() - return - } - if _, wErr := serverWriter.Write(buf[:n]); wErr != nil { - return - } - } - }() - - return &pipeRWC{Reader: clientReader, Writer: clientWriter, closers: []io.Closer{clientReader, clientWriter, serverReader, serverWriter}}, nil -} - -// pipeRWC wraps a Reader and Writer into an io.ReadWriteCloser. -type pipeRWC struct { - io.Reader - io.Writer - closers []io.Closer -} - -func (p *pipeRWC) Close() error { - for _, c := range p.closers { - _ = c.Close() - } - return nil -} - -func TestTCPListen_WithSSHTunnel(t *testing.T) { - mock := newMockTCPServer() - - // Set up the gRPC connection (needed for tcpClient even though SSH path - // won't use Forward). - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - defer func() { - _ = conn.Close() - srv.Stop() - }() - - sshMock := &mockSSHClient{} - client := newTCPClient(conn, &stubSandboxResolver{}, sshMock) - - ln, err := client.Listen( - context.Background(), "default", "my-sandbox", 8080, 0, - WithSSHTunnel(), - WithListenServiceID("ssh-svc"), - ) - require.NoError(t, err) - defer func() { _ = ln.Close() }() - - // Connect and send data through the SSH tunnel path. - c, err := net.Dial("tcp", ln.Addr().String()) - require.NoError(t, err) - - payload := []byte("ssh-tunnel-test") - _, err = c.Write(payload) - require.NoError(t, err) - - buf := make([]byte, 256) - n, err := c.Read(buf) - require.NoError(t, err) - assert.Equal(t, string(payload), string(buf[:n]), - "data should echo through SSH tunnel") - - // Verify that Tunnel was called (not Forward). - sshMock.mu.Lock() - calls := sshMock.tunnelCalls - sshMock.mu.Unlock() - assert.Equal(t, 1, calls, "SSH Tunnel should have been called exactly once") - - // Verify no Forward calls happened on the mock TCP server. - mock.mu.Lock() - initFrame := mock.lastInit - mock.mu.Unlock() - assert.Nil(t, initFrame, "TCP Forward should not have been called when using SSH tunnel") - - _ = c.Close() -} - -func TestTCPListen_CallerSpecifiedPort(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - const wantPort = 19876 - ln, err := client.Listen(context.Background(), "default", "my-sandbox", 8080, wantPort) - require.NoError(t, err) - require.NotNil(t, ln) - defer func() { _ = ln.Close() }() - - tcpAddr, ok := ln.Addr().(*net.TCPAddr) - require.True(t, ok) - assert.Equal(t, wantPort, tcpAddr.Port, "listener should bind to the exact port requested") - - c, err := net.Dial("tcp", ln.Addr().String()) - require.NoError(t, err) - defer func() { _ = c.Close() }() - - _, err = c.Write([]byte("fixed-port")) - require.NoError(t, err) - - buf := make([]byte, 64) - n, err := c.Read(buf) - require.NoError(t, err) - assert.Equal(t, "fixed-port", string(buf[:n])) -} - -func TestTCPListen_ServiceIDPropagated(t *testing.T) { - mock := newMockTCPServer() - client, cleanup := setupTCPTest(t, mock) - defer cleanup() - - ln, err := client.Listen(context.Background(), "default", "my-sandbox", 8080, 0, - WithListenServiceID("test-svc-id"), - ) - require.NoError(t, err) - defer func() { _ = ln.Close() }() - - c, err := net.Dial("tcp", ln.Addr().String()) - require.NoError(t, err) - - _, err = c.Write([]byte("svc-id-test")) - require.NoError(t, err) - buf := make([]byte, 64) - _, err = c.Read(buf) - require.NoError(t, err) - _ = c.Close() - - mock.mu.Lock() - init := mock.lastInit - mock.mu.Unlock() - - require.NotNil(t, init, "mock should have received the init frame") - assert.Equal(t, "test-svc-id", init.GetServiceId(), - "Listen should propagate service ID to the Forward init frame") -} - -func TestTCPListen_WithSSHTunnel_NilSSH(t *testing.T) { - mock := newMockTCPServer() - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - defer func() { - _ = conn.Close() - srv.Stop() - }() - - client := newTCPClient(conn, &stubSandboxResolver{}, nil) - _, err = client.Listen(context.Background(), "default", "my-sandbox", 8080, 0, WithSSHTunnel()) - require.Error(t, err) - assert.True(t, IsInvalidArgument(err), "WithSSHTunnel with nil SSH client should return InvalidArgument") -} - -// --- Failure injection helpers --- - -// flippableResolver extends stubSandboxResolver with a mutex-guarded error -// that can be toggled at runtime (set to nil to stop failing). -type flippableResolver struct { - mu sync.Mutex - failErr error -} - -func (r *flippableResolver) Get(_ context.Context, _, name string) (*Sandbox, error) { - r.mu.Lock() - defer r.mu.Unlock() - if r.failErr != nil { - return nil, r.failErr - } - return &Sandbox{ID: "sb-" + name, Name: name}, nil -} - -func (r *flippableResolver) Create(context.Context, string, string, *SandboxSpec, ...CreateOption) (*Sandbox, error) { - panic("not implemented") -} -func (r *flippableResolver) List(string, ...ListOptions) (*Pager[*Sandbox], error) { - panic("not implemented") -} -func (r *flippableResolver) ListAll(context.Context, string, ...ListOptions) ([]*Sandbox, error) { - panic("not implemented") -} -func (r *flippableResolver) Delete(context.Context, string, string, ...DeleteOptions) (*DeletionResult, error) { - panic("not implemented") -} -func (r *flippableResolver) AttachProvider(context.Context, string, string, string, uint64) (*AttachProviderResult, error) { - panic("not implemented") -} -func (r *flippableResolver) DetachProvider(context.Context, string, string, string, uint64) (*DetachProviderResult, error) { - panic("not implemented") -} -func (r *flippableResolver) ListProviders(string, string, ...ListOptions) (*Pager[*Provider], error) { - panic("not implemented") -} -func (r *flippableResolver) ListAllProviders(context.Context, string, string, ...ListOptions) ([]*Provider, error) { - panic("not implemented") -} -func (r *flippableResolver) WaitReady(context.Context, string, string, ...WaitOptions) (*Sandbox, error) { - panic("not implemented") -} -func (r *flippableResolver) Watch(context.Context, string, string, ...WatchOptions) (WatchInterface[*Sandbox], error) { - panic("not implemented") -} -func (r *flippableResolver) GetLogs(context.Context, string, string, ...LogOption) (*LogResult, error) { - panic("not implemented") -} -func (r *flippableResolver) Stop(context.Context, string, string) (*Sandbox, error) { - panic("not implemented") -} -func (r *flippableResolver) Start(context.Context, string, string) (*Sandbox, error) { - panic("not implemented") -} -func (r *flippableResolver) WaitStopped(context.Context, string, string, ...WaitOptions) (*Sandbox, error) { - panic("not implemented") -} - -// --- Failure injection tests --- - -func TestTCPListen_TunnelSetupRetry(t *testing.T) { - mock := newMockTCPServer() - - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - defer func() { - _ = conn.Close() - srv.Stop() - }() - - // Use a resolver that fails initially, then succeeds. - resolver := &flippableResolver{ - failErr: &StatusError{Code: ErrorUnavailable, Message: "sandbox unreachable"}, - } - client := newTCPClient(conn, resolver, nil) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - inner, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - - tl := &tunnelListener{ - inner: inner, - ctx: ctx, - cancel: cancel, - tcp: client, - sandboxName: "my-sandbox", - remotePort: 8080, - cfg: listenConfig{bindAddress: "127.0.0.1"}, - } - - tl.wg.Add(1) - go tl.acceptLoop() - - // First connection triggers Forward which fails (resolver returns error). - c1, err := net.Dial("tcp", inner.Addr().String()) - require.NoError(t, err) - defer func() { _ = c1.Close() }() - - time.Sleep(50 * time.Millisecond) - - // Clear the error so the next Forward succeeds. - resolver.mu.Lock() - resolver.failErr = nil - resolver.mu.Unlock() - - // Second connection should succeed through the retry loop. - c2, err := net.Dial("tcp", inner.Addr().String()) - require.NoError(t, err) - defer func() { _ = c2.Close() }() - - _, err = c2.Write([]byte("retry-ok")) - require.NoError(t, err) - buf := make([]byte, 32) - n, err := c2.Read(buf) - require.NoError(t, err) - assert.Equal(t, "retry-ok", string(buf[:n])) - - _ = tl.Close() -} - -func TestTCPListen_TunnelFailureWithContextCancel(t *testing.T) { - mock := newMockTCPServer() - - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - require.NoError(t, err) - defer func() { - _ = conn.Close() - srv.Stop() - }() - - // Resolver always fails: Forward will error on every attempt. - resolver := &flippableResolver{ - failErr: &StatusError{Code: ErrorUnavailable, Message: "permanent failure"}, - } - client := newTCPClient(conn, resolver, nil) - - ctx, cancel := context.WithCancel(context.Background()) - - ln, err := client.Listen(ctx, "default", "my-sandbox", 8080, 0) - require.NoError(t, err) - - // Trigger a connection that will fail tunnel setup. - c, err := net.Dial("tcp", ln.Addr().String()) - require.NoError(t, err) - _ = c.Close() - - // Give the internal accept loop time to enter tunnel setup. - time.Sleep(50 * time.Millisecond) - - // Cancel context: the context-watcher goroutine in Listen() calls - // Close(), which closes the inner listener and stops the accept loop. - cancel() - require.Eventually(t, func() bool { - return ln.Close() == nil - }, 5*time.Second, 10*time.Millisecond) -} - -// gatedResolver lets a test control how long tunnel setup (Get) takes. -type gatedResolver struct { - stubSandboxResolver - gate func(ctx context.Context) error -} - -func (r *gatedResolver) Get(ctx context.Context, workspace, name string) (*Sandbox, error) { - if err := r.gate(ctx); err != nil { - return nil, err - } - return r.stubSandboxResolver.Get(ctx, workspace, name) -} - -func echoOnce(addr, msg string) error { - conn, err := net.Dial("tcp", addr) - if err != nil { - return err - } - defer func() { _ = conn.Close() }() - _ = conn.SetDeadline(time.Now().Add(5 * time.Second)) - if _, err := conn.Write([]byte(msg)); err != nil { - return err - } - buf := make([]byte, len(msg)) - if _, err := io.ReadFull(conn, buf); err != nil { - return err - } - if string(buf) != msg { - return fmt.Errorf("expected %q, got %q", msg, buf) - } - return nil -} - -func TestTCPListen_ConcurrentTunnelSetup(t *testing.T) { - const numConns = 4 - var arrived atomic.Int32 - allArrived := make(chan struct{}) - gateErrs := make(chan error, numConns) - - // Each setup waits for all the others, which only completes when the - // accept loop does not serialize tunnel setup. - resolver := &gatedResolver{gate: func(ctx context.Context) error { - if arrived.Add(1) == numConns { - close(allArrived) - } - select { - case <-allArrived: - return nil - case <-ctx.Done(): - gateErrs <- ctx.Err() - return ctx.Err() - case <-time.After(2 * time.Second): - err := errors.New("tunnel setup was serialized") - gateErrs <- err - return err - } - }} - client, cleanup := setupTCPTestWithResolver(t, newMockTCPServer(), resolver) - defer cleanup() - - ln, err := client.Listen(context.Background(), "default", "my-sandbox", 8080, 0) - require.NoError(t, err) - defer func() { _ = ln.Close() }() - - errCh := make(chan error, numConns) - for i := range numConns { - go func() { errCh <- echoOnce(ln.Addr().String(), fmt.Sprintf("msg-%d", i)) }() - } - var echoErrs []error - for range numConns { - echoErrs = append(echoErrs, <-errCh) - } - select { - case err := <-gateErrs: - t.Fatalf("gate failed: %v", err) - default: - } - for _, err := range echoErrs { - require.NoError(t, err) - } -} - -// requireEOF requires that the next read on conn reports io.EOF. -func requireEOF(t *testing.T, conn net.Conn, timeout time.Duration) { - t.Helper() - require.NoError(t, conn.SetReadDeadline(time.Now().Add(timeout))) - _, err := conn.Read(make([]byte, 1)) - require.ErrorIs(t, err, io.EOF) -} - -func TestTCPListen_HungSetupDoesNotBlockOthers(t *testing.T) { - var calls atomic.Int32 - resolver := &gatedResolver{gate: func(ctx context.Context) error { - if calls.Add(1) == 1 { - <-ctx.Done() - return ctx.Err() - } - return nil - }} - client, cleanup := setupTCPTestWithResolver(t, newMockTCPServer(), resolver) - defer cleanup() - - ln, err := client.Listen(context.Background(), "default", "my-sandbox", 8080, 0) - require.NoError(t, err) - - hung, err := net.Dial("tcp", ln.Addr().String()) - require.NoError(t, err) - defer func() { _ = hung.Close() }() - require.Eventually(t, func() bool { return calls.Load() == 1 }, 5*time.Second, 5*time.Millisecond) - - require.NoError(t, echoOnce(ln.Addr().String(), "second")) - - // Close cancels the hung setup instead of waiting on it. - closed := make(chan error, 1) - go func() { closed <- ln.Close() }() - select { - case err := <-closed: - require.NoError(t, err) - case <-time.After(5 * time.Second): - t.Fatal("Close did not return while a tunnel setup was hung") - } - requireEOF(t, hung, time.Second) -} - -func TestTCPListen_FailedSetupClosesConn(t *testing.T) { - resolver := &flippableResolver{ - failErr: &StatusError{Code: ErrorUnavailable, Message: "sandbox unreachable"}, - } - client, cleanup := setupTCPTestWithResolver(t, newMockTCPServer(), resolver) - defer cleanup() - - ln, err := client.Listen(context.Background(), "default", "my-sandbox", 8080, 0) - require.NoError(t, err) - defer func() { _ = ln.Close() }() - - c, err := net.Dial("tcp", ln.Addr().String()) - require.NoError(t, err) - defer func() { _ = c.Close() }() - requireEOF(t, c, 2*time.Second) -} diff --git a/sdk/go/openshell/v1/types.go b/sdk/go/openshell/v1/types.go deleted file mode 100644 index a09acd28fa..0000000000 --- a/sdk/go/openshell/v1/types.go +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// SandboxPhase represents the lifecycle phase of a sandbox. -type SandboxPhase = types.SandboxPhase - -// SandboxPhase values for sandbox lifecycle. -const ( - SandboxProvisioning = types.SandboxProvisioning - SandboxReady = types.SandboxReady - SandboxError = types.SandboxError - SandboxDeleting = types.SandboxDeleting - SandboxUnknown = types.SandboxUnknown - SandboxStopping = types.SandboxStopping - SandboxStopped = types.SandboxStopped - SandboxStarting = types.SandboxStarting - SandboxCompleted = types.SandboxCompleted - SandboxSuspending = types.SandboxSuspending - SandboxSuspended = types.SandboxSuspended -) - -// SandboxRestartPolicy controls replacement after the canonical main process exits. -type SandboxRestartPolicy = types.SandboxRestartPolicy - -// Sandbox restart policy values. -const ( - SandboxRestartNever = types.SandboxRestartNever - SandboxRestartOnFailure = types.SandboxRestartOnFailure - SandboxRestartAlways = types.SandboxRestartAlways -) - -// EventType classifies watch events. -type EventType = types.EventType - -// EventType values for watch events. -const ( - EventAdded = types.EventAdded - EventModified = types.EventModified - EventDeleted = types.EventDeleted - EventError = types.EventError -) - -// StreamType identifies which output stream a chunk belongs to. -type StreamType = types.StreamType - -// StreamType values for exec output. -const ( - StreamStdout = types.StreamStdout - StreamStderr = types.StreamStderr -) - -// TLSConfig holds TLS connection settings. -type TLSConfig = types.TLSConfig diff --git a/sdk/go/openshell/v1/types/auth.go b/sdk/go/openshell/v1/types/auth.go deleted file mode 100644 index 90da224739..0000000000 --- a/sdk/go/openshell/v1/types/auth.go +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import "context" - -// AuthProvider supplies per-RPC credentials. It implements the -// grpc credentials.PerRPCCredentials interface. -type AuthProvider interface { - GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) - RequireTransportSecurity() bool -} diff --git a/sdk/go/openshell/v1/types/config.go b/sdk/go/openshell/v1/types/config.go deleted file mode 100644 index a0edf5b275..0000000000 --- a/sdk/go/openshell/v1/types/config.go +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import "time" - -// Config holds all settings needed to create a Client. -type Config struct { - Address string - TLS *TLSConfig - Auth AuthProvider - Timeout time.Duration - RetryPolicy *RetryPolicy - Logger Logger -} diff --git a/sdk/go/openshell/v1/types/doc.go b/sdk/go/openshell/v1/types/doc.go deleted file mode 100644 index c6577baf45..0000000000 --- a/sdk/go/openshell/v1/types/doc.go +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package types defines all domain data types for the OpenShell SDK v1 API. -// -// These types are the canonical definitions used by both the client layer -// (openshell/v1) and the converter layer (openshell/v1/internal/converter). -// The v1 package re-exports all types via type aliases for backward -// compatibility. -package types diff --git a/sdk/go/openshell/v1/types/errors.go b/sdk/go/openshell/v1/types/errors.go deleted file mode 100644 index b98fda612a..0000000000 --- a/sdk/go/openshell/v1/types/errors.go +++ /dev/null @@ -1,156 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import ( - "errors" - "fmt" - "time" -) - -// ErrorCode classifies SDK errors by their gRPC origin. -type ErrorCode int - -// ErrorCode values for classifying gRPC errors. -const ( - ErrorNotFound ErrorCode = iota + 1 - ErrorAlreadyExists - ErrorUnavailable - ErrorPermissionDenied - ErrorInvalidArgument - ErrorDeadlineExceeded - ErrorCancelled - ErrorInternal - ErrorUnimplemented - ErrorConflict - ErrorUnauthenticated -) - -// String returns the human-readable name of the error code. -func (c ErrorCode) String() string { - switch c { - case ErrorNotFound: - return "NotFound" - case ErrorAlreadyExists: - return "AlreadyExists" - case ErrorUnavailable: - return "Unavailable" - case ErrorPermissionDenied: - return "PermissionDenied" - case ErrorInvalidArgument: - return "InvalidArgument" - case ErrorDeadlineExceeded: - return "DeadlineExceeded" - case ErrorCancelled: - return "Cancelled" - case ErrorInternal: - return "Internal" - case ErrorUnimplemented: - return "Unimplemented" - case ErrorConflict: - return "Conflict" - case ErrorUnauthenticated: - return "Unauthenticated" - default: - return fmt.Sprintf("Unknown(%d)", int(c)) - } -} - -// StatusError is the typed error returned by all SDK operations. -type StatusError struct { - Code ErrorCode - Message string - Cause error - // GRPCCode retains the exact transport code before SDK classification. - GRPCCode int32 - // FieldViolations identifies invalid request fields without parsing Message. - FieldViolations []FieldViolation - // ErrorInfo is the server's machine-readable reason, domain, and metadata. - ErrorInfo *ErrorInfo - // RetryDelay is a suggested minimum delay, not proof a mutation is safe to repeat. - RetryDelay *time.Duration -} - -// FieldViolation identifies an invalid request field. -type FieldViolation struct { - Field string - Description string -} - -// ErrorInfo describes a server failure using a stable reason within a domain. -type ErrorInfo struct { - Reason string - Domain string - Metadata map[string]string -} - -func (e *StatusError) Error() string { - return fmt.Sprintf("%s: %s", e.Code, e.Message) -} - -func (e *StatusError) Unwrap() error { - return e.Cause -} - -// IsNotFound returns true if the error indicates a resource was not found. -func IsNotFound(err error) bool { - return hasCode(err, ErrorNotFound) -} - -// IsAlreadyExists returns true if the error indicates a resource already exists. -func IsAlreadyExists(err error) bool { - return hasCode(err, ErrorAlreadyExists) -} - -// IsUnavailable returns true if the error indicates the service is unavailable. -func IsUnavailable(err error) bool { - return hasCode(err, ErrorUnavailable) -} - -// IsPermissionDenied returns true if the error indicates insufficient permissions. -func IsPermissionDenied(err error) bool { - return hasCode(err, ErrorPermissionDenied) -} - -// IsInvalidArgument returns true if the error indicates an invalid argument. -func IsInvalidArgument(err error) bool { - return hasCode(err, ErrorInvalidArgument) -} - -// IsDeadlineExceeded returns true if the error indicates a deadline was exceeded. -func IsDeadlineExceeded(err error) bool { - return hasCode(err, ErrorDeadlineExceeded) -} - -// IsCancelled returns true if the error indicates the operation was cancelled. -func IsCancelled(err error) bool { - return hasCode(err, ErrorCancelled) -} - -// IsUnimplemented returns true if the error indicates the operation is not implemented. -func IsUnimplemented(err error) bool { - return hasCode(err, ErrorUnimplemented) -} - -// IsConflict returns true if the error indicates a conflict, such as -// optimistic concurrency or an invalid state transition. -func IsConflict(err error) bool { - return hasCode(err, ErrorConflict) -} - -// IsUnauthenticated returns true if the error indicates invalid or missing credentials. -func IsUnauthenticated(err error) bool { - return hasCode(err, ErrorUnauthenticated) -} - -func hasCode(err error, code ErrorCode) bool { - if err == nil { - return false - } - var se *StatusError - if errors.As(err, &se) { - return se.Code == code - } - return false -} diff --git a/sdk/go/openshell/v1/types/exec.go b/sdk/go/openshell/v1/types/exec.go deleted file mode 100644 index ecc322a2df..0000000000 --- a/sdk/go/openshell/v1/types/exec.go +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -// ExecResult holds the collected output of a completed command execution. -type ExecResult struct { - ExitCode int - Stdout []byte - Stderr []byte -} - -// ExecChunk represents a single chunk of output from a streaming command execution. -type ExecChunk struct { - Stream StreamType - Data []byte -} diff --git a/sdk/go/openshell/v1/types/health.go b/sdk/go/openshell/v1/types/health.go deleted file mode 100644 index 3039f1c80d..0000000000 --- a/sdk/go/openshell/v1/types/health.go +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -// HealthResult holds the result of a health check. -type HealthResult struct { - Healthy bool - Version string -} - -// ServiceStatus describes the health state of the gateway. -type ServiceStatus string - -// ServiceStatus constants. -const ( - ServiceStatusHealthy ServiceStatus = "Healthy" - ServiceStatusDegraded ServiceStatus = "Degraded" - ServiceStatusUnhealthy ServiceStatus = "Unhealthy" - ServiceStatusUnknown ServiceStatus = "Unknown" -) - -// GatewayInfo holds operational metadata about the gateway. -type GatewayInfo struct { - Status ServiceStatus - Version string - ComputeDrivers []ComputeDriverInfo - Extensions []ExtensionInfo -} - -// ExtensionKind identifies one supported extension family. -type ExtensionKind string - -// Extension kind constants. -const ( - ExtensionKindComputeDriver ExtensionKind = "ComputeDriver" - ExtensionKindCredentialDriver ExtensionKind = "CredentialDriver" - ExtensionKindGatewayInterceptor ExtensionKind = "GatewayInterceptor" - ExtensionKindSupervisorMiddleware ExtensionKind = "SupervisorMiddleware" - ExtensionKindUnknown ExtensionKind = "Unknown" -) - -// ExtensionInfo describes one successful gateway/extension negotiation. -type ExtensionInfo struct { - Kind ExtensionKind - ConfiguredName string - ImplementationName string - ImplementationVersion string - ProtocolMajor uint32 - ProtocolMinor uint32 - SupportedCapabilities []string - RequiredCapabilities []string -} - -// ComputeDriverInfo describes a compute backend available on the gateway. -type ComputeDriverInfo struct { - Name string - DriverName string - DriverVersion string -} - -// CurrentUser holds the authenticated caller's identity. -type CurrentUser struct { - Subject string - DisplayName string - Roles []string - Scopes []string - IdentityProvider string -} diff --git a/sdk/go/openshell/v1/types/log.go b/sdk/go/openshell/v1/types/log.go deleted file mode 100644 index 291a46004d..0000000000 --- a/sdk/go/openshell/v1/types/log.go +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import ( - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" -) - -// LogLine represents a single log entry from a sandbox. -type LogLine struct { - // Timestamp is when the log entry was recorded. - Timestamp time.Time - // Level is the log severity level (e.g., "INFO", "WARN", "ERROR"). - Level string - // Target is the log target/module. - Target string - // Message is the log message text. - Message string - // Source is the log source: "gateway" or "sandbox". - Source string - // Fields contains structured key-value fields from the tracing event. - Fields map[string]string -} - -// LogResult contains the result of a GetLogs call. -type LogResult struct { - // Lines contains the log entries in chronological order. - Lines []LogLine - // BufferTotal is the total number of lines in the server's buffer. - BufferTotal uint32 -} - -// logConfig holds configuration for GetLogs calls. -type logConfig struct { - lines uint32 - since time.Time - sources []string - minLevel string -} - -// LogOption configures a GetLogs call. -type LogOption func(*logConfig) - -// WithLogLines sets the maximum number of log lines to return. -func WithLogLines(n uint32) LogOption { - return func(c *logConfig) { - c.lines = n - } -} - -// WithLogSince filters logs to entries at or after the given time. -func WithLogSince(t time.Time) LogOption { - return func(c *logConfig) { - c.since = t - } -} - -// WithLogSources filters logs by source (e.g., "gateway", "sandbox"). -func WithLogSources(sources ...string) LogOption { - return func(c *logConfig) { - c.sources = sources - } -} - -// WithLogMinLevel sets the minimum log level to include. -func WithLogMinLevel(level string) LogOption { - return func(c *logConfig) { - c.minLevel = level - } -} - -// ApplyLogOptions applies options and returns the config. -func ApplyLogOptions(opts []LogOption) logConfig { //nolint:revive // unexported return is intentional; consumed only by v1 package - var cfg logConfig - options.Apply(&cfg, opts) - return cfg -} - -// Lines returns the configured max lines (0 means server default). -func (c *logConfig) Lines() uint32 { - return c.lines -} - -// Since returns the configured since timestamp (zero means no filter). -func (c *logConfig) Since() time.Time { - return c.since -} - -// Sources returns the configured source filters. -func (c *logConfig) Sources() []string { - return c.sources -} - -// MinLevel returns the configured minimum log level. -func (c *logConfig) MinLevel() string { - return c.minLevel -} diff --git a/sdk/go/openshell/v1/types/logger.go b/sdk/go/openshell/v1/types/logger.go deleted file mode 100644 index 351630cb0b..0000000000 --- a/sdk/go/openshell/v1/types/logger.go +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -// Logger defines structured logging for the SDK. Compatible with logr.Logger -// and slog.Logger adapters. -type Logger interface { - Debug(msg string, keysAndValues ...any) - Info(msg string, keysAndValues ...any) - Error(err error, msg string, keysAndValues ...any) -} diff --git a/sdk/go/openshell/v1/types/mutations.go b/sdk/go/openshell/v1/types/mutations.go deleted file mode 100644 index 3ed6b16d84..0000000000 --- a/sdk/go/openshell/v1/types/mutations.go +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -// DeletionOutcome distinguishes completion from asynchronous acceptance. -// Unknown numeric values are preserved and must not be treated as completion. -type DeletionOutcome int32 - -// Known deletion outcomes. Only Completed and AlreadyAbsent establish completion. -const ( - DeletionUnspecified DeletionOutcome = iota - DeletionCompleted - DeletionAccepted - DeletionAlreadyAbsent -) - -// DeletionResult describes the original target, not a same-name replacement. -type DeletionResult struct { - Outcome DeletionOutcome - // SandboxID is empty for non-sandbox deletions and missing targets. - SandboxID string -} - -// DeleteOptions configures the missing-target contract. -type DeleteOptions struct { - AllowMissing bool -} diff --git a/sdk/go/openshell/v1/types/network_policy.go b/sdk/go/openshell/v1/types/network_policy.go deleted file mode 100644 index 0e413483f7..0000000000 --- a/sdk/go/openshell/v1/types/network_policy.go +++ /dev/null @@ -1,244 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -// NetworkPolicyRule defines a named network policy rule containing endpoints and binaries. -type NetworkPolicyRule struct { - // Name is the map key for this rule in the sandbox policy. - Name string - // Endpoints lists the network endpoints governed by this rule. - Endpoints []PolicyNetworkEndpoint - // Binaries lists the binaries governed by this rule. - Binaries []PolicyNetworkBinary -} - -// NetworkTLSMode controls TLS handling for a policy endpoint. -type NetworkTLSMode int32 - -const ( - // NetworkTLSModeUnspecified uses automatic TLS handling. - NetworkTLSModeUnspecified NetworkTLSMode = 0 - // NetworkTLSModeSkip disables TLS inspection. - NetworkTLSModeSkip NetworkTLSMode = 1 - // NetworkTLSModeTerminate is rejected by policy validation. - NetworkTLSModeTerminate NetworkTLSMode = 2 - // NetworkTLSModePassthrough is rejected by policy validation. - NetworkTLSModePassthrough NetworkTLSMode = 3 -) - -// NetworkEnforcementMode controls whether an endpoint audits or enforces L7 rules. -type NetworkEnforcementMode int32 - -const ( - // NetworkEnforcementModeUnspecified uses the documented audit default. - NetworkEnforcementModeUnspecified NetworkEnforcementMode = 0 - // NetworkEnforcementModeEnforce blocks policy violations. - NetworkEnforcementModeEnforce NetworkEnforcementMode = 1 - // NetworkEnforcementModeAudit logs policy violations without blocking them. - NetworkEnforcementModeAudit NetworkEnforcementMode = 2 -) - -// NetworkAccessPreset selects a predefined endpoint access policy. -type NetworkAccessPreset int32 - -const ( - // NetworkAccessPresetUnspecified selects no access preset. - NetworkAccessPresetUnspecified NetworkAccessPreset = 0 - // NetworkAccessPresetReadOnly permits read operations. - NetworkAccessPresetReadOnly NetworkAccessPreset = 1 - // NetworkAccessPresetReadWrite permits read and write operations. - NetworkAccessPresetReadWrite NetworkAccessPreset = 2 - // NetworkAccessPresetFull permits every operation supported by the protocol. - NetworkAccessPresetFull NetworkAccessPreset = 3 -) - -// PolicyNetworkEndpoint describes a full network endpoint with its access controls -// as used in sandbox network policy rules. This is distinct from [NetworkEndpoint] -// which is the simplified profile-level endpoint (Host, Port, Protocol only). -type PolicyNetworkEndpoint struct { - Host string - Port uint32 - Ports []uint32 - Protocol string - TLS NetworkTLSMode - Enforcement NetworkEnforcementMode - Access NetworkAccessPreset - Rules []L7Rule - AllowedIPs []string - DenyRules []L7DenyRule - AllowEncodedSlash bool - PersistedQueries string - GraphqlPersistedQueries map[string]GraphqlOperation - GraphqlMaxBodyBytes uint32 - Path string - WebsocketCredentialRewrite bool - RequestBodyCredentialRewrite bool - // AllowUninspectedCredentials explicitly permits credential-bearing traffic - // on paths OpenShell cannot inspect or rewrite. - AllowUninspectedCredentials bool - // ProviderCredentialed is gateway-derived provenance indicating that the - // endpoint belongs to an attached credentialed provider. - ProviderCredentialed bool - AdvisorProposed bool - CredentialSigning string - SigningService string - SigningRegion string - JSONRPCMaxBodyBytes uint32 - Mcp *McpOptions - CredentialBinding *NetworkCredentialBinding -} - -// NetworkCredentialBinding binds an endpoint to static credentials from an attached provider. -type NetworkCredentialBinding struct { - Provider string -} - -// PolicyNetworkBinary identifies a binary subject to network policy enforcement. -// This is distinct from [NetworkBinary] which is the simplified profile-level binary. -type PolicyNetworkBinary struct { - // Path is the filesystem path to the binary. - Path string -} - -// L7Rule wraps an L7 allow rule. -type L7Rule struct { - // Allow holds the layer-7 allow criteria. - Allow *L7Allow -} - -// L7Allow specifies layer-7 allow criteria for HTTP/GraphQL traffic. -type L7Allow struct { - Method string - Path string - Command string - Query map[string]L7QueryMatcher - OperationType string - OperationName string - Fields []string - Params map[string]L7QueryMatcher -} - -// L7DenyRule specifies layer-7 deny criteria for HTTP/GraphQL traffic. -type L7DenyRule struct { - Method string - Path string - Command string - Query map[string]L7QueryMatcher - OperationType string - OperationName string - Fields []string - Params map[string]L7QueryMatcher -} - -// L7QueryMatcher matches query parameters by glob pattern or exact values. -type L7QueryMatcher struct { - Glob string - Any []string -} - -// McpOptions configures MCP-specific policy controls on a network endpoint. -type McpOptions struct { - StrictToolNames *bool - AllowAllKnownMcpMethods *bool - // Versions lists the exact MCP protocol revisions accepted by the endpoint. - // An empty list represents omission at the protobuf transport boundary; the - // checked policy or server ingress materializes the pinned default - // "2025-11-25" before storing canonical state. Nonempty lists remain exact - // allowlists. - Versions []string -} - -// GraphqlOperation describes a GraphQL operation for persisted-query validation. -type GraphqlOperation struct { - OperationType string - OperationName string - Fields []string -} - -// --- MergeOperation types --- - -// PolicyMergeOperation represents a single atomic policy mutation. -// Exactly one of the pointer fields must be non-nil, modelling the proto oneof. -type PolicyMergeOperation struct { - // AddRule adds a new named network policy rule. - AddRule *AddNetworkRule - // RemoveEndpoint removes a single endpoint from a rule. - RemoveEndpoint *RemoveNetworkEndpoint - // RemoveRule removes an entire named rule. - RemoveRule *RemoveNetworkRule - // AddDenyRules appends deny rules to an endpoint. - AddDenyRules *AddDenyRules - // AddAllowRules appends allow rules to an endpoint. - AddAllowRules *AddAllowRules - // RemoveBinary removes a binary from a rule. - RemoveBinary *RemoveNetworkBinary -} - -// AddNetworkRule adds a named network policy rule with a full rule definition. -type AddNetworkRule struct { - // RuleName is the name key for the rule. - RuleName string - // Rule is the full network policy rule to add. - Rule NetworkPolicyRule -} - -// RemoveNetworkEndpoint removes a specific endpoint from a named rule. -type RemoveNetworkEndpoint struct { - // RuleName is the name of the rule containing the endpoint. - RuleName string - // Host is the endpoint host to remove. - Host string - // Port is the endpoint port to remove. - Port uint32 -} - -// RemoveNetworkRule removes an entire named rule from the policy. -type RemoveNetworkRule struct { - // RuleName is the name of the rule to remove. - RuleName string -} - -// L7RuleTarget identifies an endpoint and declares its complete affected scope. -// The gateway requires the ports and binary scope to match the stored endpoint -// and rule before appending any layer-7 rules. -type L7RuleTarget struct { - // RuleName names the base-policy rule containing the endpoint. - RuleName string - // Host is the endpoint host, matched case-insensitively. - Host string - // Ports lists every port affected by the append, not only a lookup port. - Ports []uint32 - // Path selects the endpoint path, distinct from an appended request path. - // Nil requires a unique endpoint; a pointer to "" selects an unscoped endpoint. - Path *string - // Binaries lists every binary governed by the containing rule. - // Exactly one of a nonempty Binaries list or AnyBinary=true is required. - Binaries []PolicyNetworkBinary - // AnyBinary explicitly acknowledges a rule with unrestricted binary scope. - AnyBinary bool -} - -// AddDenyRules appends layer-7 deny rules to a specific endpoint. -type AddDenyRules struct { - // Target is required and declares the full scope affected by the append. - Target *L7RuleTarget - // DenyRules are the deny rules to append. - DenyRules []L7DenyRule -} - -// AddAllowRules appends layer-7 allow rules to a specific endpoint. -type AddAllowRules struct { - // Target is required and declares the full scope affected by the append. - Target *L7RuleTarget - // Rules are the allow rules to append. - Rules []L7Rule -} - -// RemoveNetworkBinary removes a binary from a named rule. -type RemoveNetworkBinary struct { - // RuleName is the name of the rule containing the binary. - RuleName string - // BinaryPath is the filesystem path of the binary to remove. - BinaryPath string -} diff --git a/sdk/go/openshell/v1/types/options.go b/sdk/go/openshell/v1/types/options.go deleted file mode 100644 index eccf21e979..0000000000 --- a/sdk/go/openshell/v1/types/options.go +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import ( - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" -) - -// createConfig holds configuration for Create calls. -type createConfig struct { - labels map[string]string - annotations map[string]string - serviceExposures []ServiceExposure -} - -// CreateOption configures a Create call. -type CreateOption func(*createConfig) - -// WithLabels sets labels on the created resource. -func WithLabels(labels map[string]string) CreateOption { - return func(c *createConfig) { - c.labels = labels - } -} - -// WithAnnotations sets annotations on the created resource. -func WithAnnotations(annotations map[string]string) CreateOption { - return func(c *createConfig) { - c.annotations = annotations - } -} - -// WithServiceExposures registers loopback HTTP services with the created -// sandbox. Leave Service empty for the unnamed endpoint. -func WithServiceExposures(exposures ...ServiceExposure) CreateOption { - return func(c *createConfig) { - c.serviceExposures = exposures - } -} - -// ApplyCreateOptions applies options and returns the config. Nil options are -// ignored so that callers migrating from the positional labels parameter, which -// they passed as nil for an unlabeled sandbox, do not panic. -func ApplyCreateOptions(opts []CreateOption) createConfig { //nolint:revive // unexported return is intentional; consumed only by v1 package - var cfg createConfig - options.Apply(&cfg, opts) - return cfg -} - -// Labels returns the configured labels. -func (c *createConfig) Labels() map[string]string { - return c.labels -} - -// Annotations returns the configured annotations. -func (c *createConfig) Annotations() map[string]string { - return c.annotations -} - -// ServiceExposures returns the configured service exposures. -func (c *createConfig) ServiceExposures() []ServiceExposure { - return c.serviceExposures -} - -// ListOptions configures resource listing with pagination and filtering. -type ListOptions struct { - // PageSize is the maximum number of resources requested per RPC. - PageSize int - // PageToken resumes after a page returned by the same list query. - PageToken string - LabelSelector string - // AllWorkspaces selects a platform-admin view across workspace boundaries. - AllWorkspaces bool -} - -// WatchOptions configures watch behavior. -type WatchOptions struct { - // StopOnTerminal causes the watch to close automatically when the sandbox - // reaches a terminal phase (Ready or Error). - StopOnTerminal bool -} - -// ResumeOptions configures how a suspended sandbox resumes. -type ResumeOptions struct { - // RestartIfRestoreFails lets the isolation backend boot the workload - // cleanly when it cannot restore the retained memory state. When false, - // resume fails instead. - RestartIfRestoreFails bool -} - -// WaitOptions configures wait behavior. Use context for timeout control. -type WaitOptions struct { - PollInterval time.Duration -} - -// ExecOptions configures command execution. -type ExecOptions struct { - Env map[string]string - WorkDir string - // NoLoginShell skips sourcing shell login/profile startup files before the - // command. The zero value (false) preserves login-shell behavior. Set it - // for automation and managed checks that need predictable startup behavior. - NoLoginShell bool -} diff --git a/sdk/go/openshell/v1/types/options_nil_test.go b/sdk/go/openshell/v1/types/options_nil_test.go deleted file mode 100644 index d629837f54..0000000000 --- a/sdk/go/openshell/v1/types/options_nil_test.go +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestLogOption_NilIgnored(t *testing.T) { - cfg := ApplyLogOptions([]LogOption{ - WithLogLines(100), - nil, - WithLogMinLevel("error"), - }) - assert.Equal(t, uint32(100), cfg.Lines()) - assert.Equal(t, "error", cfg.MinLevel()) -} - -func TestGetDraftOption_NilIgnored(t *testing.T) { - cfg := ApplyGetDraftOptions([]GetDraftOption{ - WithStatusFilter("pending"), - nil, - WithStatusFilter("approved"), - }) - assert.Equal(t, "approved", cfg.StatusFilter()) -} diff --git a/sdk/go/openshell/v1/types/options_test.go b/sdk/go/openshell/v1/types/options_test.go deleted file mode 100644 index 972ac483d8..0000000000 --- a/sdk/go/openshell/v1/types/options_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -// Callers migrating from the positional labels parameter passed nil for an -// unlabeled sandbox. That nil still satisfies CreateOption, so it reaches -// ApplyCreateOptions and must not be invoked. -func TestApplyCreateOptions_IgnoresNilOption(t *testing.T) { - cfg := ApplyCreateOptions([]CreateOption{nil}) - - assert.Nil(t, cfg.Labels()) - assert.Nil(t, cfg.Annotations()) -} - -func TestApplyCreateOptions_IgnoresNilAmongRealOptions(t *testing.T) { - cfg := ApplyCreateOptions([]CreateOption{ - nil, - WithLabels(map[string]string{"env": "dev"}), - nil, - WithAnnotations(map[string]string{"source": "cli"}), - nil, - }) - - assert.Equal(t, map[string]string{"env": "dev"}, cfg.Labels()) - assert.Equal(t, map[string]string{"source": "cli"}, cfg.Annotations()) -} - -func TestApplyCreateOptions_NoOptions(t *testing.T) { - cfg := ApplyCreateOptions(nil) - - assert.Nil(t, cfg.Labels()) - assert.Nil(t, cfg.Annotations()) -} diff --git a/sdk/go/openshell/v1/types/policy.go b/sdk/go/openshell/v1/types/policy.go deleted file mode 100644 index 8fb0712b60..0000000000 --- a/sdk/go/openshell/v1/types/policy.go +++ /dev/null @@ -1,417 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import ( - "time" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/options" -) - -// PolicyLoadStatus represents the load state of a policy revision. -type PolicyLoadStatus int - -const ( - // PolicyLoadStatusUnspecified is the default zero value. - PolicyLoadStatusUnspecified PolicyLoadStatus = iota - // PolicyLoadStatusPending means the policy is queued for loading. - PolicyLoadStatusPending - // PolicyLoadStatusLoaded means the policy was successfully loaded. - PolicyLoadStatusLoaded - // PolicyLoadStatusFailed means the policy failed to load. - PolicyLoadStatusFailed - // PolicyLoadStatusSuperseded means a newer revision replaced this one. - PolicyLoadStatusSuperseded -) - -// String returns the human-readable name of the load status. -func (s PolicyLoadStatus) String() string { - switch s { - case PolicyLoadStatusUnspecified: - return "Unspecified" - case PolicyLoadStatusPending: - return "Pending" - case PolicyLoadStatusLoaded: - return "Loaded" - case PolicyLoadStatusFailed: - return "Failed" - case PolicyLoadStatusSuperseded: - return "Superseded" - default: - return "Unknown" - } -} - -// PolicyChunk represents a single proposed policy change in the draft inbox. -type PolicyChunk struct { - // ID is the unique chunk identifier. - ID string - // Status is the approval status: "pending", "approved", "rejected". - Status string - // RuleName is the proposed network_policies map key. - RuleName string - // ProposedRule is the proposed network policy rule. - ProposedRule *NetworkPolicyRule - - // Rationale is a human-readable explanation of why this rule is proposed. - Rationale string - // SecurityNotes contains security concerns flagged by analysis (empty if none). - SecurityNotes string - // Confidence is the analysis confidence score (0.0-1.0). - Confidence float32 - // DenialSummaryIDs lists the IDs of denial summaries that led to this chunk. - DenialSummaryIDs []string - // CreatedAt is when the chunk was created. - CreatedAt time.Time - // DecidedAt is when the user approved/rejected (zero if undecided). - DecidedAt time.Time - // Stage is the recommendation stage: "initial" or "refined". - Stage string - // SupersedesChunkID is the initial chunk ID this refined chunk replaces. - SupersedesChunkID string - // HitCount is how many times this endpoint was seen across denial flush cycles. - HitCount int32 - // FirstSeen is the first time this endpoint was proposed. - FirstSeen time.Time - // LastSeen is the most recent time this endpoint was re-proposed. - LastSeen time.Time - // Binary is the binary path that triggered the denial. - Binary string - // ValidationResult is the prover output from gateway-side static checks. - ValidationResult string - // RejectionReason is the operator-supplied text accompanying a rejection. - RejectionReason string - // ApplicationError is the complete-candidate preflight/application failure. - ApplicationError string - // ReviewToken binds an approval to the exact evaluated candidate. - ReviewToken string - CurrentEffectivePolicyHash string - CandidateEffectivePolicyHash string - CurrentEffectivePolicy *SandboxPolicy - CandidateEffectivePolicy *SandboxPolicy -} - -// DraftChunkApproval binds one bulk approval decision to a reviewed chunk. -type DraftChunkApproval struct { - ChunkID string - ReviewToken string -} - -// DraftPolicy contains the full draft policy state returned by GetDraft. -type DraftPolicy struct { - // Chunks contains the draft policy chunks. - Chunks []PolicyChunk - // RollingSummary is an LLM-generated summary of all analysis. - RollingSummary string - // DraftVersion is the current draft version number. - DraftVersion uint64 - // LastAnalyzedAt is when the last analysis completed. - LastAnalyzedAt time.Time -} - -// SandboxPolicy is the top-level security policy configuration for a sandbox. -// It contains filesystem access rules, Landlock LSM configuration, process -// identity rules, and named network access policies. -type SandboxPolicy struct { - // Version is the policy version number. The server may override this on write. - Version uint32 - // Filesystem controls which directories the sandbox can access. - // Nil means no filesystem policy is specified. - Filesystem *FilesystemPolicy - // Landlock configures the Linux Landlock LSM. - // Nil means no landlock policy is specified. - Landlock *LandlockPolicy - // Process controls the user and group identity for sandboxed processes. - // Nil means no process policy is specified. - Process *ProcessPolicy - // NetworkPolicies contains named network access rules. - // Nil means no network policies are specified; an empty map is distinct from nil. - NetworkPolicies map[string]NetworkPolicyRule - // NetworkMiddlewares contains named middleware pipeline configurations for - // network egress. Nil means no middleware is specified; an empty map is distinct from nil. - NetworkMiddlewares map[string]NetworkMiddlewareConfig -} - -// NetworkMiddlewareConfig configures a supervisor middleware pipeline for -// network egress. Middleware configs are referenced by name in the policy. -type NetworkMiddlewareConfig struct { - Name string - Middleware string - Config map[string]any - OnError string - Endpoints *MiddlewareEndpointSelector - Order int32 -} - -// MiddlewareEndpointSelector controls which admitted destinations use a -// middleware config, using host glob patterns. -type MiddlewareEndpointSelector struct { - Include []string - Exclude []string -} - -// FilesystemPolicy controls which directories the sandbox can access -// in read-only or read-write mode. -type FilesystemPolicy struct { - // IncludeWorkdir auto-includes the working directory as read-write. - IncludeWorkdir bool - // ReadOnly is the list of read-only directory paths. - // Nil means no read-only directories; an empty slice is distinct from nil. - ReadOnly []string - // ReadWrite is the list of read-write directory paths. - // Nil means no read-write directories; an empty slice is distinct from nil. - ReadWrite []string -} - -// LandlockPolicy configures the Linux Landlock LSM for filesystem restriction enforcement. -type LandlockPolicy struct { - // Compatibility is the compatibility mode (e.g., "best_effort", "hard_requirement"). - Compatibility string -} - -// ProcessPolicy controls the user and group identity under which sandboxed processes execute. -type ProcessPolicy struct { - // RunAsUser is the user name for sandboxed processes. - RunAsUser string - // RunAsGroup is the group name for sandboxed processes. - RunAsGroup string -} - -// SandboxPolicyRevision represents a versioned policy revision for a sandbox. -type SandboxPolicyRevision struct { - // Version is the policy version (monotonically increasing per sandbox). - Version uint32 - // PolicyHash is the SHA-256 hash of the serialized policy payload. - PolicyHash string - // Status is the load status of this revision. - Status PolicyLoadStatus - // LoadError is the error message if status is Failed. - LoadError string - // CreatedAt is when this revision was created. - CreatedAt time.Time - // LoadedAt is when this revision was loaded by the sandbox. - LoadedAt time.Time - // Policy is the typed security policy for this revision. Nil when not requested or absent. - Policy *SandboxPolicy - // Provenance is immutable metadata supplied with this policy revision. - Provenance map[string]string -} - -// PolicyStatusResult contains the status of a sandbox's policy. -type PolicyStatusResult struct { - // Revision is the queried policy revision. - Revision SandboxPolicyRevision - // ActiveVersion is the currently active (loaded) policy version. - ActiveVersion uint32 -} - -// ApproveResult contains the result of approving a single draft chunk. -type ApproveResult struct { - // PolicyVersion is the new policy version after merge. - PolicyVersion uint32 - // PolicyHash is the SHA-256 hash of the new policy. - PolicyHash string -} - -// ApproveAllResult contains the result of approving all draft chunks. -type ApproveAllResult struct { - // PolicyVersion is the new policy version after merge. - PolicyVersion uint32 - // PolicyHash is the SHA-256 hash of the new policy. - PolicyHash string - // ChunksApproved is the number of chunks approved. - ChunksApproved uint32 - // ChunksSkipped is the number of chunks skipped (security-flagged). - ChunksSkipped uint32 -} - -// UndoResult contains the result of undoing a draft chunk approval. -type UndoResult struct { - // PolicyVersion is the new policy version after removal. - PolicyVersion uint32 - // PolicyHash is the SHA-256 hash of the updated policy. - PolicyHash string -} - -// ClearResult contains the result of clearing all draft chunks. -type ClearResult struct { - // ChunksCleared is the number of chunks cleared. - ChunksCleared uint32 -} - -// DraftHistoryEntry represents a single event in the draft policy history. -type DraftHistoryEntry struct { - // Timestamp is when the event occurred. - Timestamp time.Time - // EventType is the event type (e.g., "approved", "rejected", "cleared"). - EventType string - // Description is a human-readable description. - Description string - // ChunkID is the associated chunk ID (if applicable). - ChunkID string -} - -// getDraftConfig holds configuration for GetDraft calls. -type getDraftConfig struct { - statusFilter string -} - -// GetDraftOption configures a GetDraft call. -type GetDraftOption func(*getDraftConfig) - -// WithStatusFilter filters draft chunks by approval status. -func WithStatusFilter(status string) GetDraftOption { - return func(c *getDraftConfig) { - c.statusFilter = status - } -} - -// ApplyGetDraftOptions applies options and returns the config. -func ApplyGetDraftOptions(opts []GetDraftOption) getDraftConfig { //nolint:revive // unexported return is intentional; consumed only by v1 package - var cfg getDraftConfig - options.Apply(&cfg, opts) - return cfg -} - -// StatusFilter returns the configured status filter. -func (c *getDraftConfig) StatusFilter() string { - return c.statusFilter -} - -// approveAllConfig holds configuration for ApproveAllDraftChunks calls. -type approveAllConfig struct { - includeSecurityFlagged bool - approvals []DraftChunkApproval -} - -// WithDraftApprovals supplies the exact reviewed chunk tokens for bulk approval. -func WithDraftApprovals(approvals ...DraftChunkApproval) ApproveAllOption { - return func(c *approveAllConfig) { - c.approvals = append([]DraftChunkApproval(nil), approvals...) - } -} - -// ApproveAllOption configures an ApproveAllDraftChunks call. -type ApproveAllOption func(*approveAllConfig) - -// WithIncludeSecurityFlagged includes security-flagged chunks in bulk approval. -func WithIncludeSecurityFlagged() ApproveAllOption { - return func(c *approveAllConfig) { - c.includeSecurityFlagged = true - } -} - -// ApplyApproveAllOptions applies options and returns the config. -func ApplyApproveAllOptions(opts []ApproveAllOption) approveAllConfig { //nolint:revive // unexported return is intentional; consumed only by v1 package - var cfg approveAllConfig - options.Apply(&cfg, opts) - return cfg -} - -// IncludeSecurityFlagged returns whether security-flagged chunks are included. -func (c *approveAllConfig) IncludeSecurityFlagged() bool { - return c.includeSecurityFlagged -} - -// Approvals returns a copy of the configured token-bound approvals. -func (c *approveAllConfig) Approvals() []DraftChunkApproval { - return append([]DraftChunkApproval(nil), c.approvals...) -} - -// getStatusConfig holds configuration for GetStatus calls. -type getStatusConfig struct { - version uint32 - global bool -} - -// GetStatusOption configures a GetStatus call. -type GetStatusOption func(*getStatusConfig) - -// WithVersion queries a specific policy version instead of the latest. -func WithVersion(version uint32) GetStatusOption { - return func(c *getStatusConfig) { - c.version = version - } -} - -// WithStatusGlobal enables global policy mode on GetStatus. When true, -// the query retrieves gateway-global policy status instead of sandbox-scoped -// status, and the sandbox name and workspace parameters are ignored. -func WithStatusGlobal(global bool) GetStatusOption { - return func(c *getStatusConfig) { - c.global = global - } -} - -// ApplyGetStatusOptions applies options and returns the config. -func ApplyGetStatusOptions(opts []GetStatusOption) getStatusConfig { //nolint:revive // unexported return is intentional; consumed only by v1 package - var cfg getStatusConfig - options.Apply(&cfg, opts) - return cfg -} - -// Version returns the configured version (0 means latest). -func (c *getStatusConfig) Version() uint32 { - return c.version -} - -// Global returns whether global policy mode is enabled. -func (c *getStatusConfig) Global() bool { - return c.global -} - -// listPolicyConfig holds configuration for List calls. -type listPolicyConfig struct { - pageSize int32 - pageToken string - global bool -} - -// ListPolicyOption configures a List call. -type ListPolicyOption func(*listPolicyConfig) - -// WithPageSize sets the maximum revisions requested per page. -func WithPageSize(pageSize int32) ListPolicyOption { - return func(c *listPolicyConfig) { - c.pageSize = pageSize - } -} - -// WithPageToken resumes listing from an opaque token returned by a previous page. -func WithPageToken(pageToken string) ListPolicyOption { - return func(c *listPolicyConfig) { - c.pageToken = pageToken - } -} - -// WithListGlobal enables global policy mode on List. When true, the query -// retrieves gateway-global policy revisions instead of sandbox-scoped ones, -// and the workspace parameter is ignored. -func WithListGlobal(global bool) ListPolicyOption { - return func(c *listPolicyConfig) { - c.global = global - } -} - -// ApplyListPolicyOptions applies options and returns the config. -func ApplyListPolicyOptions(opts []ListPolicyOption) listPolicyConfig { //nolint:revive // unexported return is intentional; consumed only by v1 package - var cfg listPolicyConfig - options.Apply(&cfg, opts) - return cfg -} - -// PageSize returns the configured page size (0 means server default). -func (c *listPolicyConfig) PageSize() int32 { - return c.pageSize -} - -// PageToken returns the configured initial continuation token. -func (c *listPolicyConfig) PageToken() string { - return c.pageToken -} - -// Global returns whether global policy mode is enabled. -func (c *listPolicyConfig) Global() bool { - return c.global -} diff --git a/sdk/go/openshell/v1/types/profile.go b/sdk/go/openshell/v1/types/profile.go deleted file mode 100644 index 1589eb2ab9..0000000000 --- a/sdk/go/openshell/v1/types/profile.go +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -// ProfileCategory classifies a provider profile. -type ProfileCategory string - -// ProfileCategory values. -const ( - ProfileCategoryOther ProfileCategory = "Other" - ProfileCategoryInference ProfileCategory = "Inference" - ProfileCategoryAgent ProfileCategory = "Agent" - ProfileCategorySourceControl ProfileCategory = "SourceControl" - ProfileCategoryMessaging ProfileCategory = "Messaging" - ProfileCategoryData ProfileCategory = "Data" - ProfileCategoryKnowledge ProfileCategory = "Knowledge" -) - -// ProviderProfile defines a provider type template with credentials schema, -// files, endpoints, binaries, and discovery configuration. -type ProviderProfile struct { - ID string - DisplayName string - Description string - Category ProfileCategory - Credentials []ProfileCredential - // Files is EXPERIMENTAL. This API and its behavior may change or be removed. - Files []ProfileFile - Endpoints []NetworkEndpoint - Binaries []NetworkBinary - InferenceCapable bool - Discovery ProfileDiscovery - ResourceVersion uint64 - Annotations map[string]string - Source string - Scope string -} - -// ProfileFile declares non-secret content served at a virtual sandbox path. -// EXPERIMENTAL: This API and its behavior may change or be removed. -type ProfileFile struct { - Path string - Content string - EnvVar string -} - -// ProfileCredential defines a single credential required by a provider profile. -type ProfileCredential struct { - Name string - Description string - EnvVars []string - Required bool - // StablePlaceholder uses an opaque identity-bound resolver handle across - // value-only external provider updates. It does not change secret - // classification or bypass endpoint authorization. - StablePlaceholder bool - Secret bool - Refresh *ProfileCredentialRefresh - AuthStyle string - HeaderName string - QueryParam string - PathTemplate string - TokenGrant *CredentialTokenGrant -} - -// ProfileCredentialRefresh declares how a profile credential is refreshed. -type ProfileCredentialRefresh struct { - Strategy RefreshStrategy - TokenURL string - Scopes []string - // RefreshBefore retains the exact protobuf duration, including presence and nanoseconds. - RefreshBefore *ProfileDuration - // MaxLifetime retains the exact protobuf duration, including presence and nanoseconds. - MaxLifetime *ProfileDuration - Material []ProfileCredentialRefreshMaterial - AdditionalOutputs []ProfileCredentialRefreshOutput -} - -// ProfileCredentialRefreshMaterial declares one input required by a refresh strategy. -type ProfileCredentialRefreshMaterial struct { - Name string - Description string - Required bool - Secret bool -} - -// ProfileCredentialRefreshOutput maps a minted output to another credential. -type ProfileCredentialRefreshOutput struct { - Output string - Credential string -} - -// CredentialTokenGrantType describes which OAuth2 grant type to use for dynamic credentials. -type CredentialTokenGrantType string - -// CredentialTokenGrantType values. -const ( - CredentialTokenGrantTypeClientCredentials CredentialTokenGrantType = "ClientCredentials" - CredentialTokenGrantTypeTokenExchange CredentialTokenGrantType = "TokenExchange" -) - -// CredentialTokenGrant configures dynamic credential acquisition via OAuth2 grant. -type CredentialTokenGrant struct { - TokenEndpoint string - Audience string - JWTSVIDAudience string - Scopes []string - // CacheTTL retains the exact protobuf duration, including presence and nanoseconds. - CacheTTL *ProfileDuration - AudienceOverrides []TokenGrantAudienceOverride - ClientAssertionType string - GrantType CredentialTokenGrantType - SubjectToken *TokenGrantSubjectToken - RequestedTokenType string -} - -// ProfileDuration represents a protobuf duration without importing generated -// protobuf packages into the curated SDK types. A nil pointer means absent; a -// non-nil zero value means an explicitly present zero duration. -type ProfileDuration struct { - Seconds int64 - Nanos int32 -} - -// TokenGrantSubjectToken configures the subject token for token exchange grants. -type TokenGrantSubjectToken struct { - Source string - Credential string - SubjectTokenType string -} - -// TokenGrantAudienceOverride selects an endpoint-specific resource audience. -type TokenGrantAudienceOverride struct { - Host string - Port uint32 - Path string - Audience string - Scopes []string -} - -// NetworkEndpoint describes a network endpoint provided by a profile. -type NetworkEndpoint struct { - Host string - Port uint32 - Protocol string -} - -// NetworkBinary describes a binary artifact provided by a profile. -type NetworkBinary struct { - Path string -} - -// ProfileDiscovery holds local discovery configuration for a profile. -type ProfileDiscovery struct { - Credentials []string -} - -// ProfileImportItem is an item submitted for profile import or lint validation. -type ProfileImportItem struct { - Profile ProviderProfile - Source string -} - -// ProfileDiagnostic is a validation finding from Import, Update, or Lint. -type ProfileDiagnostic struct { - Source string - ProfileID string - Field string - Message string - Severity string -} - -// ImportResult holds the result of a profile import operation. -type ImportResult struct { - Diagnostics []ProfileDiagnostic - Profiles []ProviderProfile - Imported bool -} - -// UpdateResult holds the result of a profile update operation. -type UpdateResult struct { - Diagnostics []ProfileDiagnostic - Profile *ProviderProfile - Updated bool -} - -// LintResult holds the result of a profile lint operation. -type LintResult struct { - Diagnostics []ProfileDiagnostic - Valid bool -} diff --git a/sdk/go/openshell/v1/types/provider.go b/sdk/go/openshell/v1/types/provider.go deleted file mode 100644 index 7b8e4c2ef0..0000000000 --- a/sdk/go/openshell/v1/types/provider.go +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import "time" - -// Provider represents an AI provider registration. -type Provider struct { - ID string - Name string - Type string - CreatedAt time.Time - Labels map[string]string - Annotations map[string]string - ResourceVersion uint64 - Workspace string - DeletionTimestamp *time.Time - Spec ProviderSpec -} - -// ProviderSpec holds provider-specific configuration and credentials. -type ProviderSpec struct { - Credentials map[string]string - Config map[string]string - CredentialExpiresAt map[string]time.Time - ProfileWorkspace string - CredentialHandles map[string]CredentialHandle -} - -// CredentialHandle is an opaque handle for a provider credential stored by -// gateway credential storage. Handles are created by OpenShell and are not -// accepted as user-authored input. -type CredentialHandle struct { - Driver string - Handle string - Metadata map[string]string -} diff --git a/sdk/go/openshell/v1/types/refresh.go b/sdk/go/openshell/v1/types/refresh.go deleted file mode 100644 index 33cf56060b..0000000000 --- a/sdk/go/openshell/v1/types/refresh.go +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import "time" - -// RefreshStrategy describes how credentials are refreshed. -type RefreshStrategy string - -// RefreshStrategy values. -const ( - RefreshStrategyStatic RefreshStrategy = "Static" - RefreshStrategyExternal RefreshStrategy = "External" - RefreshStrategyOAuth2RefreshToken RefreshStrategy = "OAuth2RefreshToken" - RefreshStrategyOAuth2ClientCredentials RefreshStrategy = "OAuth2ClientCredentials" - RefreshStrategyGoogleServiceAccountJWT RefreshStrategy = "GoogleServiceAccountJWT" - RefreshStrategyAWSStsAssumeRole RefreshStrategy = "AWSStsAssumeRole" -) - -// RefreshRecoveryAction describes the action required after a refresh failure. -type RefreshRecoveryAction int - -const ( - // RefreshRecoveryActionUnspecified means no recovery action is required. - RefreshRecoveryActionUnspecified RefreshRecoveryAction = iota - // RefreshRecoveryActionRetry means OpenShell will retry automatically. - RefreshRecoveryActionRetry - // RefreshRecoveryActionReauthorize means the user must replace the OAuth grant. - RefreshRecoveryActionReauthorize - // RefreshRecoveryActionFixConfiguration means an operator must repair configuration. - RefreshRecoveryActionFixConfiguration - // RefreshRecoveryActionInvestigate means the failure is not recognized. - RefreshRecoveryActionInvestigate -) - -// String returns the provider-neutral recovery action name. -func (a RefreshRecoveryAction) String() string { - switch a { - case RefreshRecoveryActionUnspecified: - return "unspecified" - case RefreshRecoveryActionRetry: - return "retry" - case RefreshRecoveryActionReauthorize: - return "reauthorize" - case RefreshRecoveryActionFixConfiguration: - return "fix_configuration" - case RefreshRecoveryActionInvestigate: - return "investigate" - default: - return "unknown" - } -} - -// RefreshStatus reports the current state of credential refresh for a specific -// provider credential. -type RefreshStatus struct { - Provider string - ProviderID string - CredentialKey string - Strategy RefreshStrategy - Status string - ExpiresAt time.Time - // NextRefreshAt is zero when no automatic refresh is scheduled. Use - // RecoveryAction to distinguish a parked refresh from an unset timestamp. - NextRefreshAt time.Time - LastRefreshAt time.Time - LastError string - RecoveryAction RefreshRecoveryAction - FailureCode string - ProviderErrorSubtype string - LastErrorAt time.Time -} - -// RefreshConfig holds configuration parameters for gateway-owned credential -// refresh on a provider credential. -type RefreshConfig struct { - Provider string - CredentialKey string - Strategy RefreshStrategy - Material map[string]string - SecretMaterialKeys []string - ExpiresAt *time.Time -} diff --git a/sdk/go/openshell/v1/types/sandbox.go b/sdk/go/openshell/v1/types/sandbox.go deleted file mode 100644 index f1cedb595f..0000000000 --- a/sdk/go/openshell/v1/types/sandbox.go +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import "time" - -// Sandbox represents a sandbox instance. -type Sandbox struct { - // HostKeyFingerprint is the stable SSH identity; empty on older runtimes. - HostKeyFingerprint string - ID string - Name string - CreatedAt time.Time - Labels map[string]string - Annotations map[string]string - ResourceVersion uint64 - Workspace string - DeletionTimestamp *time.Time - CreatedFromWorkloadTemplate *SandboxWorkloadTemplateProvenance - Spec SandboxSpec - Status SandboxStatus - // ServiceURLs is populated by create operations and keyed by service name. - // The empty key identifies the unnamed service. - ServiceURLs map[string]string -} - -// SandboxSpec holds the desired state of a sandbox. -type SandboxSpec struct { - LogLevel string - Environment map[string]string - Template *SandboxTemplate - Providers []string - // GPU requests GPU resources using the active driver's default GPU assignment - // when GPUCount is nil. GPUCount implies GPU for backward compatibility. - GPU bool - GPUCount *uint32 - // Policy is the security policy for the sandbox. Nil means no policy specified. - Policy *SandboxPolicy - Command []string - TTY bool - RestartPolicy SandboxRestartPolicy -} - -// SandboxTemplate defines the container template for a sandbox. -type SandboxTemplate struct { - Image string - RuntimeClassName string - AgentSocket string - Labels map[string]string - Annotations map[string]string - Environment map[string]string - UserNamespaces *bool - Resources map[string]any - DriverConfig map[string]any -} - -// SandboxWorkloadTemplate is a reusable workspace-scoped sandbox template resource. -type SandboxWorkloadTemplate struct { - ID string - Name string - CreatedAt time.Time - Labels map[string]string - Annotations map[string]string - ResourceVersion uint64 - Workspace string - DeletionTimestamp *time.Time - Spec SandboxWorkloadTemplateSpec -} - -// SandboxWorkloadTemplateSpec holds reusable sandbox template settings. -type SandboxWorkloadTemplateSpec struct { - Workload *SandboxWorkloadConfig - DriverConfig map[string]any - DesiredServiceLevel *SandboxServiceLevel -} - -// SandboxWorkloadConfig defines the portable workload for a reusable template. -type SandboxWorkloadConfig struct { - Image string - Environment map[string]string - Resources *SandboxResources -} - -// SandboxResources defines portable sandbox resource requirements. -type SandboxResources struct { - CPU string - Memory string - // GPU requests GPU resources for template-backed sandboxes. A non-nil GPU - // with nil Count requests the active driver's default GPU assignment. - GPU *SandboxGPURequirements -} - -// SandboxGPURequirements defines template GPU requirements. -type SandboxGPURequirements struct { - Count *uint32 -} - -// SandboxServiceLevel describes desired operational characteristics. -type SandboxServiceLevel struct { - Startup *SandboxStartup -} - -// SandboxStartup describes desired startup characteristics. -type SandboxStartup struct { - ReadyWithin time.Duration - MaxBurst uint32 -} - -// SandboxWorkloadTemplateProvenance identifies the reusable template revision used to create a sandbox. -type SandboxWorkloadTemplateProvenance struct { - Name string - ResourceVersion string -} - -// SandboxStatus holds the observed state of a sandbox. -type SandboxStatus struct { - AgentPod string - AgentFd string - SandboxFd string - Phase SandboxPhase - Conditions []SandboxCondition - CurrentPolicyVersion uint32 - ExitCode *int32 - // EndpointStatuses describes configured external tool endpoints and their - // last accepted network results, independently of sandbox readiness. - EndpointStatuses []EndpointStatus - // LastTokenGrantFailure is the latest classified grant refusal for any - // destination, including requests outside the MCP endpoint inventory. - // A successful grant for the same destination and provider/credential clears - // it. Before credential selection, any successful grant to that destination - // clears it. Policy, provider, and session resets also clear it. - // Nil means no retained cause. - LastTokenGrantFailure *TokenGrantFailureObservation - ConfigurationAdmission *SandboxConfigurationAdmission - RestartCount uint32 - NextRestartAtMs int64 - MainProcessStartedAtMs int64 -} - -// TokenGrantFailureObservation records a refused grant's destination and cause. -// It describes the attempted call, not the remote server's current health. -type TokenGrantFailureObservation struct { - DestinationHost string - DestinationPort uint32 - Failure *EndpointFailure -} - -// ConfigurationAdmissionState describes validation of an effective configuration. -type ConfigurationAdmissionState string - -// Configuration admission states reported by the gateway. -const ( - ConfigurationAdmissionUnknown ConfigurationAdmissionState = "unknown" - ConfigurationAdmissionPending ConfigurationAdmissionState = "pending" - ConfigurationAdmissionAccepted ConfigurationAdmissionState = "accepted" - ConfigurationAdmissionRejected ConfigurationAdmissionState = "rejected" -) - -// SandboxConfigurationAdmission identifies a validated or rejected configuration. -// Supervisor instance fencing remains available through the raw protobuf API. -type SandboxConfigurationAdmission struct { - State ConfigurationAdmissionState - PolicyVersion uint32 - PolicyHash string - ConfigRevision uint64 - ProviderEnvRevision uint64 - Error string -} - -// EndpointStatus holds a configured tool endpoint and its last accepted network result. -// Observations aggregate configured callers across the listed ports; the result -// does not establish present availability or successful tool execution. -type EndpointStatus struct { - // EndpointID selects this endpoint without parsing its address or display text. - EndpointID string - Host string - Ports []uint32 - Path string - LastResult EndpointResult - // LastReportedAt is the RFC 3339 UTC rendering of the time when the gateway accepted the - // observation, not the request time. Retained evidence can be accepted after - // a reset. NoObservedExchange has no report timestamp. - LastReportedAt string - // LastFailure is the bounded cause of LastResult, when the supervisor - // classified it. Each failure domain names the results it explains; token - // grant failures accompany EndpointCredentialUnavailable. Each later - // observation replaces or clears it, and the resets that clear LastResult - // also clear it. Nil means no classified cause. - LastFailure *EndpointFailure -} - -// EndpointFailure explains why an observed exchange failed. It does not -// establish that the endpoint is unhealthy. No field carries remote text. -type EndpointFailure struct { - // Domain names the subsystem whose closed code set explains the failure. - Domain EndpointFailureDomain - // Code is a stable lower_snake_case code from the domain's closed set, - // such as provider_authorization_required. - Code string - // RecoveryAction is the action documented for Code. - RecoveryAction RefreshRecoveryAction - // Provider is the configured provider whose credential failed. It is empty - // when the failure happened before a credential was selected. - Provider string - // CredentialKey is the configured credential name within Provider, never a - // credential value. It is empty when Provider is empty. - CredentialKey string - // Stage is the step that observed the failure, from the domain's closed - // set, such as gateway_rpc. - Stage string - // UpstreamHTTPStatus is the HTTP status a token service returned. Nil means - // no status was received. - UpstreamHTTPStatus *uint32 - // RequestID is the supervisor-minted UUID of the refused exchange. The - // workload receives the same value in the x-openshell-request-id response - // header. - RequestID string -} - -// EndpointFailureDomain names the subsystem that explains an endpoint failure. -type EndpointFailureDomain string - -// EndpointFailureDomain values select the closed set that Code and Stage come from. -const ( - // EndpointFailureDomainUnspecified means the domain was absent or was not recognized. - EndpointFailureDomainUnspecified EndpointFailureDomain = "Unspecified" - // EndpointFailureDomainTokenGrant means a dynamic token grant for a provider - // credential failed, so OpenShell refused the request before any of it - // reached the endpoint. - EndpointFailureDomainTokenGrant EndpointFailureDomain = "TokenGrant" -) - -// EndpointResult classifies the last accepted network result for a tool endpoint. -type EndpointResult string - -// EndpointResult values describe passive observations of actual traffic. -const ( - // EndpointUnspecified means the result was absent or was not recognized. - EndpointUnspecified EndpointResult = "Unspecified" - // EndpointNoObservedExchange means the active configuration and supervisor - // session have no applicable observation. - EndpointNoObservedExchange EndpointResult = "NoObservedExchange" - // EndpointHTTPResponseReceived means an HTTP status below 400 was received. - // The response body can still contain a tool error. - EndpointHTTPResponseReceived EndpointResult = "HttpResponseReceived" - // EndpointPolicyDenied means OpenShell policy denied the request locally. - EndpointPolicyDenied EndpointResult = "PolicyDenied" - // EndpointCredentialUnavailable means a required managed credential was unavailable. - EndpointCredentialUnavailable EndpointResult = "CredentialUnavailable" - // EndpointTLSFailed means TLS setup for the upstream connection failed. - EndpointTLSFailed EndpointResult = "TlsFailed" - // EndpointTransportFailed means the transport failed before an HTTP response arrived. - EndpointTransportFailed EndpointResult = "TransportFailed" - // EndpointUpstreamRejected means the server returned an HTTP status of 400 or higher. - EndpointUpstreamRejected EndpointResult = "UpstreamRejected" -) - -// SandboxCondition describes an observed condition of a sandbox. -type SandboxCondition struct { - Type string - Status string - Reason string - Message string - LastTransitionTime string -} - -// AttachProviderResult holds the result of attaching a provider to a sandbox. -type AttachProviderResult struct { - Sandbox *Sandbox - Attached bool -} - -// DetachProviderResult holds the result of detaching a provider from a sandbox. -type DetachProviderResult struct { - Sandbox *Sandbox - Detached bool -} diff --git a/sdk/go/openshell/v1/types/service.go b/sdk/go/openshell/v1/types/service.go deleted file mode 100644 index 5ae7aa0b9e..0000000000 --- a/sdk/go/openshell/v1/types/service.go +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -// ServiceAuthorizationMode controls handling of an incoming application Authorization header. -type ServiceAuthorizationMode int32 - -const ( - // ServiceAuthorizationModeStrip removes Authorization before proxying to the sandbox service. - ServiceAuthorizationModeStrip ServiceAuthorizationMode = 1 - // ServiceAuthorizationModeBearerPassthrough forwards one valid bearer credential unchanged. - ServiceAuthorizationModeBearerPassthrough ServiceAuthorizationMode = 2 -) - -// ServiceExposure describes a loopback HTTP service to expose during sandbox creation. -type ServiceExposure struct { - Service string - TargetPort uint32 - AuthorizationMode ServiceAuthorizationMode -} - -// ServiceEndpoint represents an exposed HTTP service on a sandbox. -type ServiceEndpoint struct { - ID string - SandboxID string - Sandbox string - Name string - TargetPort uint32 - Domain bool - URL string - Workspace string - AuthorizationMode ServiceAuthorizationMode -} diff --git a/sdk/go/openshell/v1/types/setting.go b/sdk/go/openshell/v1/types/setting.go deleted file mode 100644 index 7dd8eff2f2..0000000000 --- a/sdk/go/openshell/v1/types/setting.go +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -// SettingValueType identifies which typed field of a SettingValue is active. -type SettingValueType string - -// SettingValueType constants. -const ( - SettingValueString SettingValueType = "string" - SettingValueBool SettingValueType = "bool" - SettingValueInt SettingValueType = "int" - SettingValueBytes SettingValueType = "bytes" -) - -// SettingValue is a typed setting value supporting string, bool, int64, and bytes variants. -// The Type field indicates which value field is populated. -type SettingValue struct { - Type SettingValueType - StringVal string - BoolVal bool - IntVal int64 - BytesVal []byte -} - -// SettingScope indicates whether a setting is controlled at sandbox or global level. -type SettingScope string - -// SettingScope constants. -const ( - SettingScopeUnspecified SettingScope = "" - SettingScopeSandbox SettingScope = "sandbox" - SettingScopeGlobal SettingScope = "global" -) - -// PolicySource indicates the source of the policy payload in a SandboxConfig response. -type PolicySource string - -// PolicySource constants. -const ( - PolicySourceUnspecified PolicySource = "" - PolicySourceSandbox PolicySource = "sandbox" - PolicySourceGlobal PolicySource = "global" -) - -// EffectiveSetting is a setting value paired with the scope it was resolved from. -type EffectiveSetting struct { - Value SettingValue - Scope SettingScope -} - -// SandboxConfig represents the full configuration state of a sandbox, -// including policy, effective settings, and revision metadata. -type SandboxConfig struct { - // Policy is the typed security policy for this sandbox. Nil means no policy in the response. - Policy *SandboxPolicy - // PolicyVersion is monotonically increasing per sandbox. - PolicyVersion uint32 - // PolicyHash is the SHA-256 of the serialized policy payload. - PolicyHash string - // Settings is the effective settings resolved for this sandbox. - Settings map[string]EffectiveSetting - // ConfigRevision is the fingerprint for effective config (policy + settings). - ConfigRevision uint64 - // PolicySource indicates where the policy came from (sandbox or global). - PolicySource PolicySource - // GlobalPolicyVersion is the global policy version (0 if not applicable). - GlobalPolicyVersion uint32 - // ProviderEnvRevision is the fingerprint for provider credential inputs. - ProviderEnvRevision uint64 - // PolicyValidationFailureMode is the gateway-configured posture for rejected - // policy generations ("fail_closed" or "retain_last_valid"). - PolicyValidationFailureMode string -} - -// GatewayConfig represents gateway-global settings. -type GatewayConfig struct { - // Settings is the global settings map. - Settings map[string]SettingValue - // SettingsRevision is a monotonically increasing revision for gateway-global settings. - SettingsRevision uint64 -} - -// ConfigUpdate represents a configuration mutation request. -// For sandbox-scoped updates, set Name to the sandbox name. -// For global-scoped updates, set Global to true. -type ConfigUpdate struct { - // Name is the sandbox name (required for sandbox-scoped updates). - Name string - // Policy is the typed security policy for a full policy replacement. Nil means no policy change. - Policy *SandboxPolicy - // SettingKey is a single setting key to mutate. - SettingKey string - // SettingValue is the setting value for upsert. Nil means no value change. - SettingValue *SettingValue - // DeleteSetting deletes the setting key when true. - DeleteSetting bool - // Global applies the update at gateway-global scope when true. - Global bool - // MergeOperations is a list of typed policy merge operations. - MergeOperations []PolicyMergeOperation - // ExpectedResourceVersion is for optimistic concurrency (0 = skip check). - ExpectedResourceVersion uint64 - // Annotations is caller-provided metadata for sandbox-scoped updates. - Annotations map[string]string -} - -// ConfigUpdateResult holds the result of a configuration update operation. -// Named ConfigUpdateResult to avoid collision with profile.UpdateResult. -type ConfigUpdateResult struct { - // Version is the assigned policy version. - Version uint32 - // PolicyHash is the SHA-256 of the serialized policy. - PolicyHash string - // SettingsRevision is the settings revision for the modified scope. - SettingsRevision uint64 - // Deleted is true when a setting delete removed an existing key. - Deleted bool - // Annotations contains sandbox metadata annotations after the update. - Annotations map[string]string -} diff --git a/sdk/go/openshell/v1/types/ssh.go b/sdk/go/openshell/v1/types/ssh.go deleted file mode 100644 index ec5e58e518..0000000000 --- a/sdk/go/openshell/v1/types/ssh.go +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import "fmt" - -// SSHSession represents an SSH session created for a sandbox. -// The Token field is sensitive and MUST NOT be logged or included in error messages. -// The String() method redacts the token to prevent accidental exposure via fmt or logging. -type SSHSession struct { - // SandboxID is the sandbox this session connects to. - SandboxID string - // Token is the session token for gateway tunnel authentication. - // This is a sensitive credential — treat it like an API key. - Token string - // GatewayHost is the host for SSH proxy connection. - GatewayHost string - // GatewayPort is the gateway port (1-65535). - GatewayPort uint32 - // GatewayScheme is the gateway protocol scheme ("http" or "https"). - GatewayScheme string - // HostKeyFingerprint is the optional host key fingerprint. - HostKeyFingerprint string - // ExpiresAtMs is the session expiry in milliseconds since epoch. - // Zero means no expiry. - ExpiresAtMs int64 -} - -// String returns a human-readable representation with the Token redacted. -func (s SSHSession) String() string { - return fmt.Sprintf("SSHSession{SandboxID:%s, GatewayHost:%s, GatewayPort:%d, GatewayScheme:%s, Token:[REDACTED]}", - s.SandboxID, s.GatewayHost, s.GatewayPort, s.GatewayScheme) -} diff --git a/sdk/go/openshell/v1/types/types.go b/sdk/go/openshell/v1/types/types.go deleted file mode 100644 index ce3cccb195..0000000000 --- a/sdk/go/openshell/v1/types/types.go +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import "time" - -// SandboxPhase represents the lifecycle phase of a sandbox. -type SandboxPhase string - -// SandboxPhase values for sandbox lifecycle. -const ( - SandboxProvisioning SandboxPhase = "Provisioning" - SandboxReady SandboxPhase = "Ready" - SandboxError SandboxPhase = "Error" - SandboxDeleting SandboxPhase = "Deleting" - SandboxUnknown SandboxPhase = "Unknown" - SandboxStopping SandboxPhase = "Stopping" - SandboxStopped SandboxPhase = "Stopped" - SandboxStarting SandboxPhase = "Starting" - SandboxCompleted SandboxPhase = "Completed" - SandboxSuspending SandboxPhase = "Suspending" - SandboxSuspended SandboxPhase = "Suspended" -) - -// SandboxRestartPolicy controls replacement after the canonical main process exits. -type SandboxRestartPolicy string - -// Sandbox restart policy values. -const ( - SandboxRestartNever SandboxRestartPolicy = "Never" - SandboxRestartOnFailure SandboxRestartPolicy = "OnFailure" - SandboxRestartAlways SandboxRestartPolicy = "Always" -) - -// EventType classifies watch events. -type EventType string - -// EventType values for watch events. -const ( - EventAdded EventType = "ADDED" - EventModified EventType = "MODIFIED" - EventDeleted EventType = "DELETED" - EventError EventType = "ERROR" -) - -// StreamType identifies which output stream a chunk belongs to. -type StreamType string - -// StreamType values for exec output. -const ( - StreamStdout StreamType = "stdout" - StreamStderr StreamType = "stderr" -) - -// TLSConfig holds TLS connection settings. -type TLSConfig struct { - CertFile string - KeyFile string - CAFile string - Insecure bool -} - -// RetryPolicy configures automatic retry behavior for failed RPCs. -type RetryPolicy struct { - MaxRetries int - InitialWait time.Duration - MaxWait time.Duration -} diff --git a/sdk/go/openshell/v1/types/watch.go b/sdk/go/openshell/v1/types/watch.go deleted file mode 100644 index 0a8c9d4986..0000000000 --- a/sdk/go/openshell/v1/types/watch.go +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -// Event represents a watch event carrying a resource that changed. -type Event[T any] struct { - Type EventType - Object T - Err error -} - -// WatchInterface delivers a stream of typed events. Modeled after -// k8s.io/apimachinery/pkg/watch.Interface. -type WatchInterface[T any] interface { - ResultChan() <-chan Event[T] - Stop() -} diff --git a/sdk/go/openshell/v1/types/workspace.go b/sdk/go/openshell/v1/types/workspace.go deleted file mode 100644 index 4539dc5a04..0000000000 --- a/sdk/go/openshell/v1/types/workspace.go +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package types - -import "time" - -// WorkspacePhase describes the lifecycle state of a workspace. -type WorkspacePhase string - -// WorkspacePhase constants. -const ( - WorkspaceActive WorkspacePhase = "Active" - WorkspaceTerminating WorkspacePhase = "Terminating" - WorkspaceUnknown WorkspacePhase = "Unknown" -) - -// WorkspaceRole describes a member's role within a workspace. -type WorkspaceRole string - -// WorkspaceRole constants. -const ( - WorkspaceRoleAdmin WorkspaceRole = "Admin" - WorkspaceRoleUser WorkspaceRole = "User" - WorkspaceRoleUnknown WorkspaceRole = "Unknown" -) - -// Workspace represents a logical grouping of resources. -type Workspace struct { - ID string - Name string - CreatedAt time.Time - Labels map[string]string - Annotations map[string]string - ResourceVersion uint64 - Workspace string - DeletionTimestamp *time.Time - Phase WorkspacePhase -} - -// WorkspaceMember represents a user's membership in a workspace. -type WorkspaceMember struct { - ID string - Name string - CreatedAt time.Time - Labels map[string]string - Annotations map[string]string - ResourceVersion uint64 - PrincipalSubject string - Role WorkspaceRole -} diff --git a/sdk/go/openshell/v1/types_reexport.go b/sdk/go/openshell/v1/types_reexport.go deleted file mode 100644 index 090918b6a0..0000000000 --- a/sdk/go/openshell/v1/types_reexport.go +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// --- Network Policy types --- - -// NetworkPolicyRule defines a named network policy rule containing endpoints and binaries. -type NetworkPolicyRule = types.NetworkPolicyRule - -// PolicyNetworkEndpoint describes a full network endpoint in a sandbox network policy rule. -type PolicyNetworkEndpoint = types.PolicyNetworkEndpoint - -// NetworkTLSMode controls TLS handling for a policy endpoint. -type NetworkTLSMode = types.NetworkTLSMode - -// NetworkEnforcementMode controls endpoint L7 enforcement behavior. -type NetworkEnforcementMode = types.NetworkEnforcementMode - -// NetworkAccessPreset selects a predefined endpoint access policy. -type NetworkAccessPreset = types.NetworkAccessPreset - -const ( - // NetworkTLSModeUnspecified uses automatic TLS handling. - NetworkTLSModeUnspecified = types.NetworkTLSModeUnspecified - // NetworkTLSModeSkip disables TLS inspection. - NetworkTLSModeSkip = types.NetworkTLSModeSkip - // NetworkTLSModeTerminate is rejected by policy validation. - NetworkTLSModeTerminate = types.NetworkTLSModeTerminate - // NetworkTLSModePassthrough is rejected by policy validation. - NetworkTLSModePassthrough = types.NetworkTLSModePassthrough - - // NetworkEnforcementModeUnspecified uses the documented audit default. - NetworkEnforcementModeUnspecified = types.NetworkEnforcementModeUnspecified - // NetworkEnforcementModeEnforce blocks policy violations. - NetworkEnforcementModeEnforce = types.NetworkEnforcementModeEnforce - // NetworkEnforcementModeAudit logs policy violations without blocking them. - NetworkEnforcementModeAudit = types.NetworkEnforcementModeAudit - - // NetworkAccessPresetUnspecified selects no access preset. - NetworkAccessPresetUnspecified = types.NetworkAccessPresetUnspecified - // NetworkAccessPresetReadOnly permits read operations. - NetworkAccessPresetReadOnly = types.NetworkAccessPresetReadOnly - // NetworkAccessPresetReadWrite permits read and write operations. - NetworkAccessPresetReadWrite = types.NetworkAccessPresetReadWrite - // NetworkAccessPresetFull permits every operation supported by the protocol. - NetworkAccessPresetFull = types.NetworkAccessPresetFull -) - -// PolicyNetworkBinary identifies a binary subject to network policy enforcement. -type PolicyNetworkBinary = types.PolicyNetworkBinary - -// L7Rule wraps an L7 allow rule. -type L7Rule = types.L7Rule - -// L7Allow specifies layer-7 allow criteria for HTTP/GraphQL traffic. -type L7Allow = types.L7Allow - -// L7DenyRule specifies layer-7 deny criteria for HTTP/GraphQL traffic. -type L7DenyRule = types.L7DenyRule - -// L7QueryMatcher matches query parameters by glob pattern or exact values. -type L7QueryMatcher = types.L7QueryMatcher - -// GraphqlOperation describes a GraphQL operation for persisted-query validation. -type GraphqlOperation = types.GraphqlOperation - -// --- MergeOperation types --- - -// PolicyMergeOperation represents a single atomic policy mutation. -type PolicyMergeOperation = types.PolicyMergeOperation - -// AddNetworkRule adds a named network policy rule with a full rule definition. -type AddNetworkRule = types.AddNetworkRule - -// RemoveNetworkEndpoint removes a specific endpoint from a named rule. -type RemoveNetworkEndpoint = types.RemoveNetworkEndpoint - -// RemoveNetworkRule removes an entire named rule from the policy. -type RemoveNetworkRule = types.RemoveNetworkRule - -// AddDenyRules appends layer-7 deny rules to a specific endpoint. -type AddDenyRules = types.AddDenyRules - -// L7RuleTarget identifies an endpoint and declares its complete affected scope. -type L7RuleTarget = types.L7RuleTarget - -// AddAllowRules appends layer-7 allow rules to a specific endpoint. -type AddAllowRules = types.AddAllowRules - -// RemoveNetworkBinary removes a binary from a named rule. -type RemoveNetworkBinary = types.RemoveNetworkBinary diff --git a/sdk/go/openshell/v1/watch.go b/sdk/go/openshell/v1/watch.go deleted file mode 100644 index 696d8a9bd1..0000000000 --- a/sdk/go/openshell/v1/watch.go +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "sync" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// Event represents a watch event carrying a resource that changed. -type Event[T any] = types.Event[T] - -// WatchInterface delivers a stream of typed events. Modeled after -// k8s.io/apimachinery/pkg/watch.Interface. -type WatchInterface[T any] = types.WatchInterface[T] - -type watcher[T any] struct { - result chan Event[T] - done chan struct{} - cancel context.CancelFunc - stopOnce sync.Once -} - -func newWatcher[T any](ch chan Event[T], cancel context.CancelFunc) *watcher[T] { - return &watcher[T]{ - result: ch, - done: make(chan struct{}), - cancel: cancel, - } -} - -func (w *watcher[T]) ResultChan() <-chan Event[T] { - return w.result -} - -func (w *watcher[T]) Stop() { - w.stopOnce.Do(func() { - close(w.done) - if w.cancel != nil { - w.cancel() - } - }) -} diff --git a/sdk/go/openshell/v1/watch_test.go b/sdk/go/openshell/v1/watch_test.go deleted file mode 100644 index 4f0c69ef0c..0000000000 --- a/sdk/go/openshell/v1/watch_test.go +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// newTestWatcher creates a watcher with a simulated producer goroutine that -// forwards events from src to the watcher's channel and closes it when the -// producer finishes or Stop is called. -func newTestWatcher(src <-chan Event[string]) *watcher[string] { - ch := make(chan Event[string], 10) - w := newWatcher(ch, nil) - go func() { - defer close(ch) - for { - select { - case ev, ok := <-src: - if !ok { - return - } - select { - case ch <- ev: - case <-w.done: - return - } - case <-w.done: - return - } - } - }() - return w -} - -// --- T038: WatchInterface event delivery, Stop, and error handling --- - -func TestWatcher_DeliversEvents(t *testing.T) { - src := make(chan Event[string], 10) - w := newTestWatcher(src) - - src <- Event[string]{Type: EventAdded, Object: "sandbox-1"} - src <- Event[string]{Type: EventModified, Object: "sandbox-1"} - - resultCh := w.ResultChan() - - ev1 := <-resultCh - assert.Equal(t, EventAdded, ev1.Type) - assert.Equal(t, "sandbox-1", ev1.Object) - - ev2 := <-resultCh - assert.Equal(t, EventModified, ev2.Type) - assert.Equal(t, "sandbox-1", ev2.Object) -} - -func TestWatcher_StopClosesChannel(t *testing.T) { - src := make(chan Event[string], 10) - w := newTestWatcher(src) - - w.Stop() - - select { - case _, ok := <-w.ResultChan(): - assert.False(t, ok, "channel should be closed after Stop") - case <-time.After(time.Second): - t.Fatal("timed out waiting for channel close") - } -} - -func TestWatcher_StopIsIdempotent(t *testing.T) { - src := make(chan Event[string], 10) - w := newTestWatcher(src) - - w.Stop() - w.Stop() // must not panic - - _, ok := <-w.ResultChan() - assert.False(t, ok, "channel should remain closed after second Stop") -} - -func TestWatcher_ErrorEvent(t *testing.T) { - src := make(chan Event[string], 10) - w := newTestWatcher(src) - - src <- Event[string]{Type: EventError, Object: "error details"} - - ev := <-w.ResultChan() - assert.Equal(t, EventError, ev.Type) - assert.Equal(t, "error details", ev.Object) -} - -func TestWatcher_ChannelClosesWhenSourceEnds(t *testing.T) { - src := make(chan Event[string], 10) - w := newTestWatcher(src) - - src <- Event[string]{Type: EventAdded, Object: "sb-1"} - close(src) - - ev := <-w.ResultChan() - require.Equal(t, "sb-1", ev.Object) - - select { - case _, ok := <-w.ResultChan(): - assert.False(t, ok, "channel should close when source ends") - case <-time.After(time.Second): - t.Fatal("timed out waiting for channel to close") - } -} - -func TestWatcher_DrainAfterStop(t *testing.T) { - src := make(chan Event[string], 10) - w := newTestWatcher(src) - - src <- Event[string]{Type: EventAdded, Object: "sb-1"} - - ev := <-w.ResultChan() - require.Equal(t, "sb-1", ev.Object) - - w.Stop() - - timeout := time.After(time.Second) - for { - select { - case _, ok := <-w.ResultChan(): - if !ok { - return // success: channel closed - } - case <-timeout: - t.Fatal("timed out waiting for channel to close after Stop") - } - } -} diff --git a/sdk/go/openshell/v1/workspace.go b/sdk/go/openshell/v1/workspace.go deleted file mode 100644 index 4ce48a3352..0000000000 --- a/sdk/go/openshell/v1/workspace.go +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types" -) - -// Workspace represents a logical grouping of resources. -type Workspace = types.Workspace - -// WorkspaceMember represents a user's membership in a workspace. -type WorkspaceMember = types.WorkspaceMember - -// WorkspacePhase describes the lifecycle state of a workspace. -type WorkspacePhase = types.WorkspacePhase - -// WorkspaceRole describes a member's role within a workspace. -type WorkspaceRole = types.WorkspaceRole - -// WorkspacePhase constants. -const ( - WorkspaceActive = types.WorkspaceActive - WorkspaceTerminating = types.WorkspaceTerminating - WorkspaceUnknown = types.WorkspaceUnknown -) - -// WorkspaceRole constants. -const ( - WorkspaceRoleAdmin = types.WorkspaceRoleAdmin - WorkspaceRoleUser = types.WorkspaceRoleUser - WorkspaceRoleUnknown = types.WorkspaceRoleUnknown -) - -// WorkspaceInterface defines workspace and member management operations. -type WorkspaceInterface interface { - Create(ctx context.Context, name string, labels map[string]string) (*Workspace, error) - Get(ctx context.Context, name string) (*Workspace, error) - List(opts ...ListOptions) (*Pager[*Workspace], error) - ListAll(ctx context.Context, opts ...ListOptions) ([]*Workspace, error) - Delete(ctx context.Context, name string, opts ...DeleteOptions) (*DeletionResult, error) - AddMember(ctx context.Context, workspace, principalSubject string, role WorkspaceRole) (*WorkspaceMember, error) - RemoveMember(ctx context.Context, workspace, principalSubject string, opts ...DeleteOptions) (*DeletionResult, error) - ListMembers(workspace string, opts ...ListOptions) (*Pager[*WorkspaceMember], error) - ListAllMembers(ctx context.Context, workspace string, opts ...ListOptions) ([]*WorkspaceMember, error) -} diff --git a/sdk/go/openshell/v1/workspace_client.go b/sdk/go/openshell/v1/workspace_client.go deleted file mode 100644 index 51ac25de37..0000000000 --- a/sdk/go/openshell/v1/workspace_client.go +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - - "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/internal/converter" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "google.golang.org/grpc" -) - -type workspaceClient struct { - client pb.OpenShellClient -} - -func newWorkspaceClient(conn grpc.ClientConnInterface) *workspaceClient { - return &workspaceClient{client: pb.NewOpenShellClient(conn)} -} - -func (w *workspaceClient) Create(ctx context.Context, name string, labels map[string]string) (*Workspace, error) { - if name == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "workspace name must not be empty"} - } - - resp, err := w.client.CreateWorkspace(ctx, &pb.CreateWorkspaceRequest{ - Name: name, - Labels: labels, - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.WorkspaceFromProto(resp.GetWorkspace()), nil -} - -func (w *workspaceClient) Get(ctx context.Context, name string) (*Workspace, error) { - if name == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "workspace name must not be empty"} - } - - resp, err := w.client.GetWorkspace(ctx, &pb.GetWorkspaceRequest{ - Name: name, - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.WorkspaceFromProto(resp.GetWorkspace()), nil -} - -func (w *workspaceClient) List(opts ...ListOptions) (*Pager[*Workspace], error) { - pageSize, err := listPageSize(opts) - if err != nil { - return nil, err - } - var pageToken, labelSelector string - if len(opts) > 0 { - pageToken = opts[0].PageToken - labelSelector = opts[0].LabelSelector - } - return newPager(pageToken, func(ctx context.Context, pageToken string) (*Page[*Workspace], error) { - req := &pb.ListWorkspacesRequest{PageSize: pageSize, PageToken: pageToken, LabelSelector: labelSelector} - resp, err := w.client.ListWorkspaces(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - workspaces := make([]*Workspace, 0, len(resp.GetWorkspaces())) - for _, proto := range resp.GetWorkspaces() { - workspaces = append(workspaces, converter.WorkspaceFromProto(proto)) - } - return &Page[*Workspace]{Items: workspaces, NextPageToken: resp.GetNextPageToken()}, nil - }), nil -} - -func (w *workspaceClient) ListAll(ctx context.Context, opts ...ListOptions) ([]*Workspace, error) { - pager, err := w.List(opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} - -func (w *workspaceClient) Delete(ctx context.Context, name string, opts ...DeleteOptions) (*DeletionResult, error) { - if name == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "workspace name must not be empty"} - } - - resp, err := w.client.DeleteWorkspace(ctx, &pb.DeleteWorkspaceRequest{ - AllowMissing: allowMissing(opts), - Name: name, - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return &DeletionResult{Outcome: DeletionOutcome(resp.GetOutcome())}, nil -} - -func (w *workspaceClient) AddMember(ctx context.Context, workspace, principalSubject string, role WorkspaceRole) (*WorkspaceMember, error) { - if workspace == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "workspace name must not be empty"} - } - if principalSubject == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "principal subject must not be empty"} - } - - protoRole := converter.WorkspaceRoleToProto(role) - if protoRole == pb.WorkspaceRole_WORKSPACE_ROLE_UNSPECIFIED { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "role must be Admin or User"} - } - - resp, err := w.client.AddWorkspaceMember(ctx, &pb.AddWorkspaceMemberRequest{ - WorkspaceScope: namedWorkspaceScope(workspace), - PrincipalSubject: principalSubject, - Role: protoRole, - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return converter.WorkspaceMemberFromProto(resp.GetMember()), nil -} - -func (w *workspaceClient) RemoveMember(ctx context.Context, workspace, principalSubject string, opts ...DeleteOptions) (*DeletionResult, error) { - if workspace == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "workspace name must not be empty"} - } - if principalSubject == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "principal subject must not be empty"} - } - - resp, err := w.client.RemoveWorkspaceMember(ctx, &pb.RemoveWorkspaceMemberRequest{ - AllowMissing: allowMissing(opts), - WorkspaceScope: namedWorkspaceScope(workspace), - PrincipalSubject: principalSubject, - }) - if err != nil { - return nil, converter.FromGRPCError(err) - } - return &DeletionResult{Outcome: DeletionOutcome(resp.GetOutcome())}, nil -} - -func (w *workspaceClient) ListMembers(workspace string, opts ...ListOptions) (*Pager[*WorkspaceMember], error) { - if workspace == "" { - return nil, &StatusError{Code: ErrorInvalidArgument, Message: "workspace name must not be empty"} - } - - pageSize, err := listPageSize(opts) - if err != nil { - return nil, err - } - var pageToken string - if len(opts) > 0 { - pageToken = opts[0].PageToken - } - return newPager(pageToken, func(ctx context.Context, pageToken string) (*Page[*WorkspaceMember], error) { - req := &pb.ListWorkspaceMembersRequest{WorkspaceScope: namedWorkspaceScope(workspace), PageSize: pageSize, PageToken: pageToken} - resp, err := w.client.ListWorkspaceMembers(ctx, req) - if err != nil { - return nil, converter.FromGRPCError(err) - } - members := make([]*WorkspaceMember, 0, len(resp.GetMembers())) - for _, proto := range resp.GetMembers() { - members = append(members, converter.WorkspaceMemberFromProto(proto)) - } - return &Page[*WorkspaceMember]{Items: members, NextPageToken: resp.GetNextPageToken()}, nil - }), nil -} - -func (w *workspaceClient) ListAllMembers(ctx context.Context, workspace string, opts ...ListOptions) ([]*WorkspaceMember, error) { - pager, err := w.ListMembers(workspace, opts...) - if err != nil { - return nil, err - } - return pager.All(ctx) -} diff --git a/sdk/go/openshell/v1/workspace_scope.go b/sdk/go/openshell/v1/workspace_scope.go deleted file mode 100644 index b3e237ef9b..0000000000 --- a/sdk/go/openshell/v1/workspace_scope.go +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - -func namedWorkspaceScope(workspace string) *dm.WorkspaceSelector { - return &dm.WorkspaceSelector{ - Selection: &dm.WorkspaceSelector_Workspace{Workspace: workspace}, - } -} - -func profileWorkspaceScope(workspace string) *dm.WorkspaceSelector { - if workspace == "" { - return nil - } - return namedWorkspaceScope(workspace) -} - -func allWorkspacesScope() *dm.WorkspaceSelector { - return &dm.WorkspaceSelector{ - Selection: &dm.WorkspaceSelector_AllWorkspaces{AllWorkspaces: &dm.AllWorkspaces{}}, - } -} diff --git a/sdk/go/openshell/v1/workspace_test.go b/sdk/go/openshell/v1/workspace_test.go deleted file mode 100644 index a052f71b1a..0000000000 --- a/sdk/go/openshell/v1/workspace_test.go +++ /dev/null @@ -1,495 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package v1 - -import ( - "context" - "net" - "testing" - "time" - - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" - "google.golang.org/protobuf/types/known/timestamppb" -) - -type mockWorkspaceServer struct { - pb.UnimplementedOpenShellServer - - createResp *pb.CreateWorkspaceResponse - getResp *pb.GetWorkspaceResponse - listResp *pb.ListWorkspacesResponse - deleteResp *pb.DeleteWorkspaceResponse - addMemberResp *pb.AddWorkspaceMemberResponse - removeMemberResp *pb.RemoveWorkspaceMemberResponse - listMembersResp *pb.ListWorkspaceMembersResponse - err error - lastCreateReq *pb.CreateWorkspaceRequest - lastListReq *pb.ListWorkspacesRequest - lastAddMemberReq *pb.AddWorkspaceMemberRequest - lastListMembersReq *pb.ListWorkspaceMembersRequest -} - -func (s *mockWorkspaceServer) CreateWorkspace(_ context.Context, req *pb.CreateWorkspaceRequest) (*pb.CreateWorkspaceResponse, error) { - s.lastCreateReq = req - if s.err != nil { - return nil, s.err - } - return s.createResp, nil -} - -func (s *mockWorkspaceServer) GetWorkspace(_ context.Context, _ *pb.GetWorkspaceRequest) (*pb.GetWorkspaceResponse, error) { - if s.err != nil { - return nil, s.err - } - return s.getResp, nil -} - -func (s *mockWorkspaceServer) ListWorkspaces(_ context.Context, req *pb.ListWorkspacesRequest) (*pb.ListWorkspacesResponse, error) { - s.lastListReq = req - if s.err != nil { - return nil, s.err - } - return s.listResp, nil -} - -func (s *mockWorkspaceServer) DeleteWorkspace(_ context.Context, _ *pb.DeleteWorkspaceRequest) (*pb.DeleteWorkspaceResponse, error) { - if s.err != nil { - return nil, s.err - } - return s.deleteResp, nil -} - -func (s *mockWorkspaceServer) AddWorkspaceMember(_ context.Context, req *pb.AddWorkspaceMemberRequest) (*pb.AddWorkspaceMemberResponse, error) { - s.lastAddMemberReq = req - if s.err != nil { - return nil, s.err - } - return s.addMemberResp, nil -} - -func (s *mockWorkspaceServer) RemoveWorkspaceMember(_ context.Context, _ *pb.RemoveWorkspaceMemberRequest) (*pb.RemoveWorkspaceMemberResponse, error) { - if s.err != nil { - return nil, s.err - } - return s.removeMemberResp, nil -} - -func (s *mockWorkspaceServer) ListWorkspaceMembers(_ context.Context, req *pb.ListWorkspaceMembersRequest) (*pb.ListWorkspaceMembersResponse, error) { - s.lastListMembersReq = req - if s.err != nil { - return nil, s.err - } - return s.listMembersResp, nil -} - -func newMockWorkspaceServer(mock *mockWorkspaceServer) (*grpc.ClientConn, func()) { - lis := bufconn.Listen(bufSize) - srv := grpc.NewServer() - pb.RegisterOpenShellServer(srv, mock) - - go func() { _ = srv.Serve(lis) }() - - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - if err != nil { - srv.Stop() - panic("grpc.NewClient failed: " + err.Error()) - } - - return conn, func() { - _ = conn.Close() - srv.Stop() - } -} - -func testWorkspace() *dm.Workspace { - return &dm.Workspace{ - Metadata: &dm.ObjectMeta{ - Id: "ws-1", - Name: "test-ws", - CreatedTime: timestamppb.New(time.UnixMilli(1700000000000)), - Labels: map[string]string{"team": "platform"}, - ResourceVersion: 1, - }, - Status: &dm.WorkspaceStatus{ - Phase: dm.WorkspacePhase_WORKSPACE_PHASE_ACTIVE, - }, - } -} - -func TestWorkspaceCreate_Success(t *testing.T) { - mock := &mockWorkspaceServer{ - createResp: &pb.CreateWorkspaceResponse{Workspace: testWorkspace()}, - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - ws, err := wc.Create(context.Background(), "test-ws", map[string]string{"team": "platform"}) - - require.NoError(t, err) - require.NotNil(t, ws) - assert.Equal(t, "test-ws", ws.Name) - assert.Equal(t, WorkspaceActive, ws.Phase) - assert.Equal(t, map[string]string{"team": "platform"}, ws.Labels) - assert.Equal(t, "test-ws", mock.lastCreateReq.GetName()) -} - -func TestWorkspaceCreate_EmptyName(t *testing.T) { - mock := &mockWorkspaceServer{} - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.Create(context.Background(), "", nil) - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestWorkspaceCreate_AlreadyExists(t *testing.T) { - mock := &mockWorkspaceServer{ - err: status.Error(codes.AlreadyExists, "workspace already exists"), - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.Create(context.Background(), "existing-ws", nil) - - require.Error(t, err) - assert.True(t, IsAlreadyExists(err)) -} - -func TestWorkspaceGet_Success(t *testing.T) { - mock := &mockWorkspaceServer{ - getResp: &pb.GetWorkspaceResponse{Workspace: testWorkspace()}, - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - ws, err := wc.Get(context.Background(), "test-ws") - - require.NoError(t, err) - require.NotNil(t, ws) - assert.Equal(t, "test-ws", ws.Name) -} - -func TestWorkspaceGet_EmptyName(t *testing.T) { - mock := &mockWorkspaceServer{} - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.Get(context.Background(), "") - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestWorkspaceGet_NotFound(t *testing.T) { - mock := &mockWorkspaceServer{ - err: status.Error(codes.NotFound, "workspace not found"), - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.Get(context.Background(), "missing-ws") - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestWorkspaceList_Success(t *testing.T) { - mock := &mockWorkspaceServer{ - listResp: &pb.ListWorkspacesResponse{ - Workspaces: []*dm.Workspace{testWorkspace()}, - }, - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - workspaces, err := wc.ListAll(context.Background()) - - require.NoError(t, err) - require.Len(t, workspaces, 1) - assert.Equal(t, "test-ws", workspaces[0].Name) -} - -func TestWorkspaceList_WithOptions(t *testing.T) { - mock := &mockWorkspaceServer{ - listResp: &pb.ListWorkspacesResponse{}, - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.ListAll(context.Background(), ListOptions{ - PageSize: 10, - LabelSelector: "team=platform", - }) - - require.NoError(t, err) - assert.Equal(t, int32(10), mock.lastListReq.GetPageSize()) - assert.Empty(t, mock.lastListReq.GetPageToken()) - assert.Equal(t, "team=platform", mock.lastListReq.GetLabelSelector()) -} - -func TestWorkspaceList_EmptyReturnsNonNilSlice(t *testing.T) { - mock := &mockWorkspaceServer{listResp: &pb.ListWorkspacesResponse{}} - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - workspaces, err := newWorkspaceClient(conn).ListAll(context.Background()) - - require.NoError(t, err) - assert.NotNil(t, workspaces) - assert.Empty(t, workspaces) -} - -func TestWorkspaceDelete_Success(t *testing.T) { - mock := &mockWorkspaceServer{ - deleteResp: &pb.DeleteWorkspaceResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_COMPLETED}, - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.Delete(context.Background(), "test-ws") - - require.NoError(t, err) -} - -func TestWorkspaceDelete_EmptyName(t *testing.T) { - mock := &mockWorkspaceServer{} - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.Delete(context.Background(), "") - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestWorkspaceDelete_NotFound(t *testing.T) { - mock := &mockWorkspaceServer{ - err: status.Error(codes.NotFound, "workspace not found"), - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.Delete(context.Background(), "missing-ws") - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -// --- Member management tests --- - -func testMember() *pb.WorkspaceMember { - return &pb.WorkspaceMember{ - Metadata: &dm.ObjectMeta{ - Id: "mem-1", - Name: "member-auto", - CreatedTime: timestamppb.New(time.UnixMilli(1700000000000)), - ResourceVersion: 1, - }, - PrincipalSubject: "user@example.com", - Role: pb.WorkspaceRole_WORKSPACE_ROLE_ADMIN, - } -} - -func TestAddMember_Success(t *testing.T) { - mock := &mockWorkspaceServer{ - addMemberResp: &pb.AddWorkspaceMemberResponse{Member: testMember()}, - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - m, err := wc.AddMember(context.Background(), "test-ws", "user@example.com", WorkspaceRoleAdmin) - - require.NoError(t, err) - require.NotNil(t, m) - assert.Equal(t, "user@example.com", m.PrincipalSubject) - assert.Equal(t, WorkspaceRoleAdmin, m.Role) - assert.Equal(t, "test-ws", mock.lastAddMemberReq.GetWorkspaceScope().GetWorkspace()) - assert.Equal(t, "user@example.com", mock.lastAddMemberReq.GetPrincipalSubject()) -} - -func TestAddMember_EmptyWorkspace(t *testing.T) { - mock := &mockWorkspaceServer{} - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.AddMember(context.Background(), "", "user@example.com", WorkspaceRoleAdmin) - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestAddMember_EmptySubject(t *testing.T) { - mock := &mockWorkspaceServer{} - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.AddMember(context.Background(), "test-ws", "", WorkspaceRoleAdmin) - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestAddMember_InvalidRole(t *testing.T) { - mock := &mockWorkspaceServer{} - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.AddMember(context.Background(), "test-ws", "user@example.com", WorkspaceRole("invalid")) - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestAddMember_AlreadyExists(t *testing.T) { - mock := &mockWorkspaceServer{ - err: status.Error(codes.AlreadyExists, "member already exists"), - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.AddMember(context.Background(), "test-ws", "user@example.com", WorkspaceRoleUser) - - require.Error(t, err) - assert.True(t, IsAlreadyExists(err)) -} - -func TestRemoveMember_Success(t *testing.T) { - mock := &mockWorkspaceServer{ - removeMemberResp: &pb.RemoveWorkspaceMemberResponse{Outcome: pb.DeletionOutcome_DELETION_OUTCOME_COMPLETED}, - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.RemoveMember(context.Background(), "test-ws", "user@example.com") - - require.NoError(t, err) -} - -func TestRemoveMember_EmptyWorkspace(t *testing.T) { - mock := &mockWorkspaceServer{} - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.RemoveMember(context.Background(), "", "user@example.com") - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestRemoveMember_EmptySubject(t *testing.T) { - mock := &mockWorkspaceServer{} - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.RemoveMember(context.Background(), "test-ws", "") - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestRemoveMember_NotFound(t *testing.T) { - mock := &mockWorkspaceServer{ - err: status.Error(codes.NotFound, "member not found"), - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.RemoveMember(context.Background(), "test-ws", "missing@example.com") - - require.Error(t, err) - assert.True(t, IsNotFound(err)) -} - -func TestListMembers_Success(t *testing.T) { - mock := &mockWorkspaceServer{ - listMembersResp: &pb.ListWorkspaceMembersResponse{ - Members: []*pb.WorkspaceMember{testMember()}, - }, - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - members, err := wc.ListAllMembers(context.Background(), "test-ws") - - require.NoError(t, err) - require.Len(t, members, 1) - assert.Equal(t, "user@example.com", members[0].PrincipalSubject) -} - -func TestListMembers_EmptyWorkspace(t *testing.T) { - mock := &mockWorkspaceServer{} - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.ListAllMembers(context.Background(), "") - - require.Error(t, err) - assert.True(t, IsInvalidArgument(err)) -} - -func TestListMembers_EmptyResultReturnsNonNilSlice(t *testing.T) { - mock := &mockWorkspaceServer{ - listMembersResp: &pb.ListWorkspaceMembersResponse{}, - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - members, err := newWorkspaceClient(conn).ListAllMembers(context.Background(), "test-ws") - - require.NoError(t, err) - assert.NotNil(t, members) - assert.Empty(t, members) -} - -func TestListMembers_WithOptions(t *testing.T) { - mock := &mockWorkspaceServer{ - listMembersResp: &pb.ListWorkspaceMembersResponse{}, - } - conn, cleanup := newMockWorkspaceServer(mock) - defer cleanup() - - wc := newWorkspaceClient(conn) - _, err := wc.ListAllMembers(context.Background(), "test-ws", ListOptions{PageSize: 5}) - - require.NoError(t, err) - assert.Equal(t, int32(5), mock.lastListMembersReq.GetPageSize()) - assert.Empty(t, mock.lastListMembersReq.GetPageToken()) -} diff --git a/sdk/go/proto/datamodelv1/datamodel.pb.go b/sdk/go/proto/datamodelv1/datamodel.pb.go deleted file mode 100644 index 917f9bbc59..0000000000 --- a/sdk/go/proto/datamodelv1/datamodel.pb.go +++ /dev/null @@ -1,746 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: datamodel.proto - -package datamodelv1 - -import ( - _ "github.com/NVIDIA/OpenShell/sdk/go/proto/optionsv1" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Phase of a workspace's lifecycle. -type WorkspacePhase int32 - -const ( - WorkspacePhase_WORKSPACE_PHASE_UNSPECIFIED WorkspacePhase = 0 - WorkspacePhase_WORKSPACE_PHASE_ACTIVE WorkspacePhase = 1 - WorkspacePhase_WORKSPACE_PHASE_TERMINATING WorkspacePhase = 2 -) - -// Enum value maps for WorkspacePhase. -var ( - WorkspacePhase_name = map[int32]string{ - 0: "WORKSPACE_PHASE_UNSPECIFIED", - 1: "WORKSPACE_PHASE_ACTIVE", - 2: "WORKSPACE_PHASE_TERMINATING", - } - WorkspacePhase_value = map[string]int32{ - "WORKSPACE_PHASE_UNSPECIFIED": 0, - "WORKSPACE_PHASE_ACTIVE": 1, - "WORKSPACE_PHASE_TERMINATING": 2, - } -) - -func (x WorkspacePhase) Enum() *WorkspacePhase { - p := new(WorkspacePhase) - *p = x - return p -} - -func (x WorkspacePhase) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (WorkspacePhase) Descriptor() protoreflect.EnumDescriptor { - return file_datamodel_proto_enumTypes[0].Descriptor() -} - -func (WorkspacePhase) Type() protoreflect.EnumType { - return &file_datamodel_proto_enumTypes[0] -} - -func (x WorkspacePhase) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use WorkspacePhase.Descriptor instead. -func (WorkspacePhase) EnumDescriptor() ([]byte, []int) { - return file_datamodel_proto_rawDescGZIP(), []int{0} -} - -// Selects the workspace scope for a public API request. -// -// Requests that operate on one workspace require a non-empty `workspace`. -// Cross-workspace list requests additionally accept `all_workspaces`. The -// containing request documents which selections it supports; an omitted -// selector is invalid for workspace-scoped operations. -type WorkspaceSelector struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Selection: - // - // *WorkspaceSelector_Workspace - // *WorkspaceSelector_AllWorkspaces - Selection isWorkspaceSelector_Selection `protobuf_oneof:"selection"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WorkspaceSelector) Reset() { - *x = WorkspaceSelector{} - mi := &file_datamodel_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WorkspaceSelector) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WorkspaceSelector) ProtoMessage() {} - -func (x *WorkspaceSelector) ProtoReflect() protoreflect.Message { - mi := &file_datamodel_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WorkspaceSelector.ProtoReflect.Descriptor instead. -func (*WorkspaceSelector) Descriptor() ([]byte, []int) { - return file_datamodel_proto_rawDescGZIP(), []int{0} -} - -func (x *WorkspaceSelector) GetSelection() isWorkspaceSelector_Selection { - if x != nil { - return x.Selection - } - return nil -} - -func (x *WorkspaceSelector) GetWorkspace() string { - if x != nil { - if x, ok := x.Selection.(*WorkspaceSelector_Workspace); ok { - return x.Workspace - } - } - return "" -} - -func (x *WorkspaceSelector) GetAllWorkspaces() *AllWorkspaces { - if x != nil { - if x, ok := x.Selection.(*WorkspaceSelector_AllWorkspaces); ok { - return x.AllWorkspaces - } - } - return nil -} - -type isWorkspaceSelector_Selection interface { - isWorkspaceSelector_Selection() -} - -type WorkspaceSelector_Workspace struct { - // One explicitly named workspace. Use `default` to select the gateway's - // default workspace; an empty name is invalid. - Workspace string `protobuf:"bytes,1,opt,name=workspace,proto3,oneof"` -} - -type WorkspaceSelector_AllWorkspaces struct { - // All workspaces the caller is authorized to access. Only supported by - // requests that explicitly document cross-workspace behavior. - AllWorkspaces *AllWorkspaces `protobuf:"bytes,2,opt,name=all_workspaces,json=allWorkspaces,proto3,oneof"` -} - -func (*WorkspaceSelector_Workspace) isWorkspaceSelector_Selection() {} - -func (*WorkspaceSelector_AllWorkspaces) isWorkspaceSelector_Selection() {} - -// Marker for the all-workspaces selector variant. -type AllWorkspaces struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AllWorkspaces) Reset() { - *x = AllWorkspaces{} - mi := &file_datamodel_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AllWorkspaces) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AllWorkspaces) ProtoMessage() {} - -func (x *AllWorkspaces) ProtoReflect() protoreflect.Message { - mi := &file_datamodel_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AllWorkspaces.ProtoReflect.Descriptor instead. -func (*AllWorkspaces) Descriptor() ([]byte, []int) { - return file_datamodel_proto_rawDescGZIP(), []int{1} -} - -// Kubernetes-style metadata shared by all top-level OpenShell domain objects. -// -// This structure provides consistent metadata (identity, labels, annotations, -// timestamps, resource versioning) across Sandbox, Provider, SshSession, and -// other resources. -type ObjectMeta struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Stable object ID generated by the gateway. - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - // Human-readable object name (unique per object type). - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // Time when the object was created. - CreatedTime *timestamppb.Timestamp `protobuf:"bytes,103,opt,name=created_time,json=createdTime,proto3" json:"created_time,omitempty"` - // Key-value labels for filtering and organization. - // Labels must follow Kubernetes conventions: alphanumeric + `-._/`, max 63 chars per segment. - Labels map[string]string `protobuf:"bytes,4,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Optimistic concurrency control version. - // Incremented by the gateway on each update. Clients can use this for compare-and-swap operations. - ResourceVersion uint64 `protobuf:"varint,5,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` - // Opaque key-value metadata that is not used for selectors. - // Annotation keys use the same qualified-key shape as labels, but values may be longer. - Annotations map[string]string `protobuf:"bytes,6,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Workspace that owns this resource. Empty is normalized to "default" by the - // gateway. Immutable after creation. - Workspace string `protobuf:"bytes,7,opt,name=workspace,proto3" json:"workspace,omitempty"` - // Time when graceful deletion was initiated. Absence means the object is - // not being deleted. Once set, this field is - // immutable — the only path forward is completing deletion. - DeletionTime *timestamppb.Timestamp `protobuf:"bytes,108,opt,name=deletion_time,json=deletionTime,proto3" json:"deletion_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ObjectMeta) Reset() { - *x = ObjectMeta{} - mi := &file_datamodel_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ObjectMeta) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ObjectMeta) ProtoMessage() {} - -func (x *ObjectMeta) ProtoReflect() protoreflect.Message { - mi := &file_datamodel_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ObjectMeta.ProtoReflect.Descriptor instead. -func (*ObjectMeta) Descriptor() ([]byte, []int) { - return file_datamodel_proto_rawDescGZIP(), []int{2} -} - -func (x *ObjectMeta) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *ObjectMeta) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ObjectMeta) GetCreatedTime() *timestamppb.Timestamp { - if x != nil { - return x.CreatedTime - } - return nil -} - -func (x *ObjectMeta) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - -func (x *ObjectMeta) GetResourceVersion() uint64 { - if x != nil { - return x.ResourceVersion - } - return 0 -} - -func (x *ObjectMeta) GetAnnotations() map[string]string { - if x != nil { - return x.Annotations - } - return nil -} - -func (x *ObjectMeta) GetWorkspace() string { - if x != nil { - return x.Workspace - } - return "" -} - -func (x *ObjectMeta) GetDeletionTime() *timestamppb.Timestamp { - if x != nil { - return x.DeletionTime - } - return nil -} - -// Status of a workspace. -type WorkspaceStatus struct { - state protoimpl.MessageState `protogen:"open.v1"` - Phase WorkspacePhase `protobuf:"varint,1,opt,name=phase,proto3,enum=openshell.datamodel.v1.WorkspacePhase" json:"phase,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WorkspaceStatus) Reset() { - *x = WorkspaceStatus{} - mi := &file_datamodel_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WorkspaceStatus) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WorkspaceStatus) ProtoMessage() {} - -func (x *WorkspaceStatus) ProtoReflect() protoreflect.Message { - mi := &file_datamodel_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WorkspaceStatus.ProtoReflect.Descriptor instead. -func (*WorkspaceStatus) Descriptor() ([]byte, []int) { - return file_datamodel_proto_rawDescGZIP(), []int{3} -} - -func (x *WorkspaceStatus) GetPhase() WorkspacePhase { - if x != nil { - return x.Phase - } - return WorkspacePhase_WORKSPACE_PHASE_UNSPECIFIED -} - -// Workspace resource. A hard isolation boundary for sandboxes, providers, and -// other workspace-scoped resources. -type Workspace struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Kubernetes-style metadata (id, name, labels, timestamps, resource version). - // The workspace field in this ObjectMeta is unused (a workspace does not - // belong to another workspace). - Metadata *ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - // Current lifecycle status. - Status *WorkspaceStatus `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Workspace) Reset() { - *x = Workspace{} - mi := &file_datamodel_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Workspace) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Workspace) ProtoMessage() {} - -func (x *Workspace) ProtoReflect() protoreflect.Message { - mi := &file_datamodel_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Workspace.ProtoReflect.Descriptor instead. -func (*Workspace) Descriptor() ([]byte, []int) { - return file_datamodel_proto_rawDescGZIP(), []int{4} -} - -func (x *Workspace) GetMetadata() *ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *Workspace) GetStatus() *WorkspaceStatus { - if x != nil { - return x.Status - } - return nil -} - -// Opaque handle for a provider credential stored by gateway credential storage. -// Handles are created by OpenShell and must not be authored by users. -type CredentialHandle struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Internal storage owner or credential driver that owns this handle. - Driver string `protobuf:"bytes,1,opt,name=driver,proto3" json:"driver,omitempty"` - // Owner-owned opaque handle string. - Handle string `protobuf:"bytes,2,opt,name=handle,proto3" json:"handle,omitempty"` - // Owner-owned non-secret metadata. - Metadata map[string]string `protobuf:"bytes,3,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CredentialHandle) Reset() { - *x = CredentialHandle{} - mi := &file_datamodel_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CredentialHandle) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CredentialHandle) ProtoMessage() {} - -func (x *CredentialHandle) ProtoReflect() protoreflect.Message { - mi := &file_datamodel_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CredentialHandle.ProtoReflect.Descriptor instead. -func (*CredentialHandle) Descriptor() ([]byte, []int) { - return file_datamodel_proto_rawDescGZIP(), []int{5} -} - -func (x *CredentialHandle) GetDriver() string { - if x != nil { - return x.Driver - } - return "" -} - -func (x *CredentialHandle) GetHandle() string { - if x != nil { - return x.Handle - } - return "" -} - -func (x *CredentialHandle) GetMetadata() map[string]string { - if x != nil { - return x.Metadata - } - return nil -} - -// Provider model stored by OpenShell. -type Provider struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Kubernetes-style metadata (id, name, labels, timestamps, resource version). - Metadata *ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - // ID of the provider profile this provider was created from, matched - // exactly (for example: "claude-code", "github"). - Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` - // Secret values used for authentication. - Credentials map[string]string `protobuf:"bytes,3,rep,name=credentials,proto3" json:"credentials,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Non-secret provider configuration. - Config map[string]string `protobuf:"bytes,4,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Expiration times for credential values, keyed by credential/env var name. - // A missing key means the credential does not expire. - CredentialExpirationTimes map[string]*timestamppb.Timestamp `protobuf:"bytes,105,rep,name=credential_expiration_times,json=credentialExpirationTimes,proto3" json:"credential_expiration_times,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Workspace where this provider's type profile is stored. - // Empty string = platform/global scope. Must be empty or match - // metadata.workspace; cross-workspace references are rejected. - ProfileWorkspace string `protobuf:"bytes,6,opt,name=profile_workspace,json=profileWorkspace,proto3" json:"profile_workspace,omitempty"` - // Opaque handles for secret values stored through gateway credential storage. - // This map is internal gateway state and is not accepted as user-authored input. - CredentialHandles map[string]*CredentialHandle `protobuf:"bytes,7,rep,name=credential_handles,json=credentialHandles,proto3" json:"credential_handles,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Provider) Reset() { - *x = Provider{} - mi := &file_datamodel_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Provider) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Provider) ProtoMessage() {} - -func (x *Provider) ProtoReflect() protoreflect.Message { - mi := &file_datamodel_proto_msgTypes[6] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Provider.ProtoReflect.Descriptor instead. -func (*Provider) Descriptor() ([]byte, []int) { - return file_datamodel_proto_rawDescGZIP(), []int{6} -} - -func (x *Provider) GetMetadata() *ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *Provider) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -func (x *Provider) GetCredentials() map[string]string { - if x != nil { - return x.Credentials - } - return nil -} - -func (x *Provider) GetConfig() map[string]string { - if x != nil { - return x.Config - } - return nil -} - -func (x *Provider) GetCredentialExpirationTimes() map[string]*timestamppb.Timestamp { - if x != nil { - return x.CredentialExpirationTimes - } - return nil -} - -func (x *Provider) GetProfileWorkspace() string { - if x != nil { - return x.ProfileWorkspace - } - return "" -} - -func (x *Provider) GetCredentialHandles() map[string]*CredentialHandle { - if x != nil { - return x.CredentialHandles - } - return nil -} - -var File_datamodel_proto protoreflect.FileDescriptor - -const file_datamodel_proto_rawDesc = "" + - "\n" + - "\x0fdatamodel.proto\x12\x16openshell.datamodel.v1\x1a\roptions.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x90\x01\n" + - "\x11WorkspaceSelector\x12\x1e\n" + - "\tworkspace\x18\x01 \x01(\tH\x00R\tworkspace\x12N\n" + - "\x0eall_workspaces\x18\x02 \x01(\v2%.openshell.datamodel.v1.AllWorkspacesH\x00R\rallWorkspacesB\v\n" + - "\tselection\"\x0f\n" + - "\rAllWorkspaces\"\xc5\x04\n" + - "\n" + - "ObjectMeta\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\x12=\n" + - "\fcreated_time\x18g \x01(\v2\x1a.google.protobuf.TimestampR\vcreatedTime\x12F\n" + - "\x06labels\x18\x04 \x03(\v2..openshell.datamodel.v1.ObjectMeta.LabelsEntryR\x06labels\x12)\n" + - "\x10resource_version\x18\x05 \x01(\x04R\x0fresourceVersion\x12U\n" + - "\vannotations\x18\x06 \x03(\v23.openshell.datamodel.v1.ObjectMeta.AnnotationsEntryR\vannotations\x12\x1c\n" + - "\tworkspace\x18\a \x01(\tR\tworkspace\x12?\n" + - "\rdeletion_time\x18l \x01(\v2\x1a.google.protobuf.TimestampR\fdeletionTime\x1a9\n" + - "\vLabelsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a>\n" + - "\x10AnnotationsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01J\x04\b\x03\x10\x04J\x04\b\b\x10\tR\rcreated_at_msR\x15deletion_timestamp_ms\"O\n" + - "\x0fWorkspaceStatus\x12<\n" + - "\x05phase\x18\x01 \x01(\x0e2&.openshell.datamodel.v1.WorkspacePhaseR\x05phase\"\x8c\x01\n" + - "\tWorkspace\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12?\n" + - "\x06status\x18\x02 \x01(\v2'.openshell.datamodel.v1.WorkspaceStatusR\x06status\"\xd3\x01\n" + - "\x10CredentialHandle\x12\x16\n" + - "\x06driver\x18\x01 \x01(\tR\x06driver\x12\x16\n" + - "\x06handle\x18\x02 \x01(\tR\x06handle\x12R\n" + - "\bmetadata\x18\x03 \x03(\v26.openshell.datamodel.v1.CredentialHandle.MetadataEntryR\bmetadata\x1a;\n" + - "\rMetadataEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x8a\a\n" + - "\bProvider\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12\x12\n" + - "\x04type\x18\x02 \x01(\tR\x04type\x12Y\n" + - "\vcredentials\x18\x03 \x03(\v21.openshell.datamodel.v1.Provider.CredentialsEntryB\x04\x88\xb5\x18\x01R\vcredentials\x12D\n" + - "\x06config\x18\x04 \x03(\v2,.openshell.datamodel.v1.Provider.ConfigEntryR\x06config\x12\x7f\n" + - "\x1bcredential_expiration_times\x18i \x03(\v2?.openshell.datamodel.v1.Provider.CredentialExpirationTimesEntryR\x19credentialExpirationTimes\x12+\n" + - "\x11profile_workspace\x18\x06 \x01(\tR\x10profileWorkspace\x12f\n" + - "\x12credential_handles\x18\a \x03(\v27.openshell.datamodel.v1.Provider.CredentialHandlesEntryR\x11credentialHandles\x1a>\n" + - "\x10CredentialsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a9\n" + - "\vConfigEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1ah\n" + - "\x1eCredentialExpirationTimesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x120\n" + - "\x05value\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x05value:\x028\x01\x1an\n" + - "\x16CredentialHandlesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12>\n" + - "\x05value\x18\x02 \x01(\v2(.openshell.datamodel.v1.CredentialHandleR\x05value:\x028\x01J\x04\b\x05\x10\x06R\x18credential_expires_at_ms*n\n" + - "\x0eWorkspacePhase\x12\x1f\n" + - "\x1bWORKSPACE_PHASE_UNSPECIFIED\x10\x00\x12\x1a\n" + - "\x16WORKSPACE_PHASE_ACTIVE\x10\x01\x12\x1f\n" + - "\x1bWORKSPACE_PHASE_TERMINATING\x10\x02b\x06proto3" - -var ( - file_datamodel_proto_rawDescOnce sync.Once - file_datamodel_proto_rawDescData []byte -) - -func file_datamodel_proto_rawDescGZIP() []byte { - file_datamodel_proto_rawDescOnce.Do(func() { - file_datamodel_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_datamodel_proto_rawDesc), len(file_datamodel_proto_rawDesc))) - }) - return file_datamodel_proto_rawDescData -} - -var file_datamodel_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_datamodel_proto_msgTypes = make([]protoimpl.MessageInfo, 14) -var file_datamodel_proto_goTypes = []any{ - (WorkspacePhase)(0), // 0: openshell.datamodel.v1.WorkspacePhase - (*WorkspaceSelector)(nil), // 1: openshell.datamodel.v1.WorkspaceSelector - (*AllWorkspaces)(nil), // 2: openshell.datamodel.v1.AllWorkspaces - (*ObjectMeta)(nil), // 3: openshell.datamodel.v1.ObjectMeta - (*WorkspaceStatus)(nil), // 4: openshell.datamodel.v1.WorkspaceStatus - (*Workspace)(nil), // 5: openshell.datamodel.v1.Workspace - (*CredentialHandle)(nil), // 6: openshell.datamodel.v1.CredentialHandle - (*Provider)(nil), // 7: openshell.datamodel.v1.Provider - nil, // 8: openshell.datamodel.v1.ObjectMeta.LabelsEntry - nil, // 9: openshell.datamodel.v1.ObjectMeta.AnnotationsEntry - nil, // 10: openshell.datamodel.v1.CredentialHandle.MetadataEntry - nil, // 11: openshell.datamodel.v1.Provider.CredentialsEntry - nil, // 12: openshell.datamodel.v1.Provider.ConfigEntry - nil, // 13: openshell.datamodel.v1.Provider.CredentialExpirationTimesEntry - nil, // 14: openshell.datamodel.v1.Provider.CredentialHandlesEntry - (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp -} -var file_datamodel_proto_depIdxs = []int32{ - 2, // 0: openshell.datamodel.v1.WorkspaceSelector.all_workspaces:type_name -> openshell.datamodel.v1.AllWorkspaces - 15, // 1: openshell.datamodel.v1.ObjectMeta.created_time:type_name -> google.protobuf.Timestamp - 8, // 2: openshell.datamodel.v1.ObjectMeta.labels:type_name -> openshell.datamodel.v1.ObjectMeta.LabelsEntry - 9, // 3: openshell.datamodel.v1.ObjectMeta.annotations:type_name -> openshell.datamodel.v1.ObjectMeta.AnnotationsEntry - 15, // 4: openshell.datamodel.v1.ObjectMeta.deletion_time:type_name -> google.protobuf.Timestamp - 0, // 5: openshell.datamodel.v1.WorkspaceStatus.phase:type_name -> openshell.datamodel.v1.WorkspacePhase - 3, // 6: openshell.datamodel.v1.Workspace.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 4, // 7: openshell.datamodel.v1.Workspace.status:type_name -> openshell.datamodel.v1.WorkspaceStatus - 10, // 8: openshell.datamodel.v1.CredentialHandle.metadata:type_name -> openshell.datamodel.v1.CredentialHandle.MetadataEntry - 3, // 9: openshell.datamodel.v1.Provider.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 11, // 10: openshell.datamodel.v1.Provider.credentials:type_name -> openshell.datamodel.v1.Provider.CredentialsEntry - 12, // 11: openshell.datamodel.v1.Provider.config:type_name -> openshell.datamodel.v1.Provider.ConfigEntry - 13, // 12: openshell.datamodel.v1.Provider.credential_expiration_times:type_name -> openshell.datamodel.v1.Provider.CredentialExpirationTimesEntry - 14, // 13: openshell.datamodel.v1.Provider.credential_handles:type_name -> openshell.datamodel.v1.Provider.CredentialHandlesEntry - 15, // 14: openshell.datamodel.v1.Provider.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp - 6, // 15: openshell.datamodel.v1.Provider.CredentialHandlesEntry.value:type_name -> openshell.datamodel.v1.CredentialHandle - 16, // [16:16] is the sub-list for method output_type - 16, // [16:16] is the sub-list for method input_type - 16, // [16:16] is the sub-list for extension type_name - 16, // [16:16] is the sub-list for extension extendee - 0, // [0:16] is the sub-list for field type_name -} - -func init() { file_datamodel_proto_init() } -func file_datamodel_proto_init() { - if File_datamodel_proto != nil { - return - } - file_datamodel_proto_msgTypes[0].OneofWrappers = []any{ - (*WorkspaceSelector_Workspace)(nil), - (*WorkspaceSelector_AllWorkspaces)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_datamodel_proto_rawDesc), len(file_datamodel_proto_rawDesc)), - NumEnums: 1, - NumMessages: 14, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_datamodel_proto_goTypes, - DependencyIndexes: file_datamodel_proto_depIdxs, - EnumInfos: file_datamodel_proto_enumTypes, - MessageInfos: file_datamodel_proto_msgTypes, - }.Build() - File_datamodel_proto = out.File - file_datamodel_proto_goTypes = nil - file_datamodel_proto_depIdxs = nil -} diff --git a/sdk/go/proto/openshellv1/openshell.pb.go b/sdk/go/proto/openshellv1/openshell.pb.go deleted file mode 100644 index bb665f05ad..0000000000 --- a/sdk/go/proto/openshellv1/openshell.pb.go +++ /dev/null @@ -1,22553 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: openshell.proto - -package openshellv1 - -import ( - datamodelv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - _ "github.com/NVIDIA/OpenShell/sdk/go/proto/optionsv1" - sandboxv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - durationpb "google.golang.org/protobuf/types/known/durationpb" - structpb "google.golang.org/protobuf/types/known/structpb" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type ExtensionKind int32 - -const ( - ExtensionKind_EXTENSION_KIND_UNSPECIFIED ExtensionKind = 0 - ExtensionKind_EXTENSION_KIND_COMPUTE_DRIVER ExtensionKind = 1 - ExtensionKind_EXTENSION_KIND_CREDENTIAL_DRIVER ExtensionKind = 2 - ExtensionKind_EXTENSION_KIND_GATEWAY_INTERCEPTOR ExtensionKind = 3 - ExtensionKind_EXTENSION_KIND_SUPERVISOR_MIDDLEWARE ExtensionKind = 4 -) - -// Enum value maps for ExtensionKind. -var ( - ExtensionKind_name = map[int32]string{ - 0: "EXTENSION_KIND_UNSPECIFIED", - 1: "EXTENSION_KIND_COMPUTE_DRIVER", - 2: "EXTENSION_KIND_CREDENTIAL_DRIVER", - 3: "EXTENSION_KIND_GATEWAY_INTERCEPTOR", - 4: "EXTENSION_KIND_SUPERVISOR_MIDDLEWARE", - } - ExtensionKind_value = map[string]int32{ - "EXTENSION_KIND_UNSPECIFIED": 0, - "EXTENSION_KIND_COMPUTE_DRIVER": 1, - "EXTENSION_KIND_CREDENTIAL_DRIVER": 2, - "EXTENSION_KIND_GATEWAY_INTERCEPTOR": 3, - "EXTENSION_KIND_SUPERVISOR_MIDDLEWARE": 4, - } -) - -func (x ExtensionKind) Enum() *ExtensionKind { - p := new(ExtensionKind) - *p = x - return p -} - -func (x ExtensionKind) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ExtensionKind) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[0].Descriptor() -} - -func (ExtensionKind) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[0] -} - -func (x ExtensionKind) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ExtensionKind.Descriptor instead. -func (ExtensionKind) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{0} -} - -// High-level sandbox lifecycle phase derived by the gateway. -// -// Clients should rely on this normalized lifecycle summary for readiness and -// deletion decisions instead of interpreting raw conditions. -type SandboxPhase int32 - -const ( - SandboxPhase_SANDBOX_PHASE_UNSPECIFIED SandboxPhase = 0 - SandboxPhase_SANDBOX_PHASE_PROVISIONING SandboxPhase = 1 - SandboxPhase_SANDBOX_PHASE_READY SandboxPhase = 2 - SandboxPhase_SANDBOX_PHASE_ERROR SandboxPhase = 3 - SandboxPhase_SANDBOX_PHASE_DELETING SandboxPhase = 4 - SandboxPhase_SANDBOX_PHASE_UNKNOWN SandboxPhase = 5 - SandboxPhase_SANDBOX_PHASE_STOPPING SandboxPhase = 6 - SandboxPhase_SANDBOX_PHASE_STOPPED SandboxPhase = 7 - SandboxPhase_SANDBOX_PHASE_STARTING SandboxPhase = 8 - // The canonical main process exited successfully and its result is final. - SandboxPhase_SANDBOX_PHASE_COMPLETED SandboxPhase = 9 - // The gateway requested suspension and the driver has not confirmed it. - SandboxPhase_SANDBOX_PHASE_SUSPENDING SandboxPhase = 11 - // The workload is frozen and its memory state is retained by the isolation - // backend. ResumeSandbox restores it; StartSandbox discards it. - SandboxPhase_SANDBOX_PHASE_SUSPENDED SandboxPhase = 12 -) - -// Enum value maps for SandboxPhase. -var ( - SandboxPhase_name = map[int32]string{ - 0: "SANDBOX_PHASE_UNSPECIFIED", - 1: "SANDBOX_PHASE_PROVISIONING", - 2: "SANDBOX_PHASE_READY", - 3: "SANDBOX_PHASE_ERROR", - 4: "SANDBOX_PHASE_DELETING", - 5: "SANDBOX_PHASE_UNKNOWN", - 6: "SANDBOX_PHASE_STOPPING", - 7: "SANDBOX_PHASE_STOPPED", - 8: "SANDBOX_PHASE_STARTING", - 9: "SANDBOX_PHASE_COMPLETED", - 11: "SANDBOX_PHASE_SUSPENDING", - 12: "SANDBOX_PHASE_SUSPENDED", - } - SandboxPhase_value = map[string]int32{ - "SANDBOX_PHASE_UNSPECIFIED": 0, - "SANDBOX_PHASE_PROVISIONING": 1, - "SANDBOX_PHASE_READY": 2, - "SANDBOX_PHASE_ERROR": 3, - "SANDBOX_PHASE_DELETING": 4, - "SANDBOX_PHASE_UNKNOWN": 5, - "SANDBOX_PHASE_STOPPING": 6, - "SANDBOX_PHASE_STOPPED": 7, - "SANDBOX_PHASE_STARTING": 8, - "SANDBOX_PHASE_COMPLETED": 9, - "SANDBOX_PHASE_SUSPENDING": 11, - "SANDBOX_PHASE_SUSPENDED": 12, - } -) - -func (x SandboxPhase) Enum() *SandboxPhase { - p := new(SandboxPhase) - *p = x - return p -} - -func (x SandboxPhase) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (SandboxPhase) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[1].Descriptor() -} - -func (SandboxPhase) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[1] -} - -func (x SandboxPhase) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use SandboxPhase.Descriptor instead. -func (SandboxPhase) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{1} -} - -// Operation whose installed authority is tracked by a receipt. -type ProviderMutationKind int32 - -const ( - ProviderMutationKind_PROVIDER_MUTATION_KIND_UNSPECIFIED ProviderMutationKind = 0 - ProviderMutationKind_PROVIDER_MUTATION_KIND_ATTACH ProviderMutationKind = 1 - ProviderMutationKind_PROVIDER_MUTATION_KIND_DETACH ProviderMutationKind = 2 - ProviderMutationKind_PROVIDER_MUTATION_KIND_UPDATE ProviderMutationKind = 3 - // Reconstructed status for existing desired state without a mutation receipt. - ProviderMutationKind_PROVIDER_MUTATION_KIND_OBSERVE ProviderMutationKind = 4 -) - -// Enum value maps for ProviderMutationKind. -var ( - ProviderMutationKind_name = map[int32]string{ - 0: "PROVIDER_MUTATION_KIND_UNSPECIFIED", - 1: "PROVIDER_MUTATION_KIND_ATTACH", - 2: "PROVIDER_MUTATION_KIND_DETACH", - 3: "PROVIDER_MUTATION_KIND_UPDATE", - 4: "PROVIDER_MUTATION_KIND_OBSERVE", - } - ProviderMutationKind_value = map[string]int32{ - "PROVIDER_MUTATION_KIND_UNSPECIFIED": 0, - "PROVIDER_MUTATION_KIND_ATTACH": 1, - "PROVIDER_MUTATION_KIND_DETACH": 2, - "PROVIDER_MUTATION_KIND_UPDATE": 3, - "PROVIDER_MUTATION_KIND_OBSERVE": 4, - } -) - -func (x ProviderMutationKind) Enum() *ProviderMutationKind { - p := new(ProviderMutationKind) - *p = x - return p -} - -func (x ProviderMutationKind) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ProviderMutationKind) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[2].Descriptor() -} - -func (ProviderMutationKind) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[2] -} - -func (x ProviderMutationKind) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ProviderMutationKind.Descriptor instead. -func (ProviderMutationKind) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{2} -} - -// Readiness states describe persisted intent separately from installed state. -type ProviderReadinessState int32 - -const ( - ProviderReadinessState_PROVIDER_READINESS_STATE_UNSPECIFIED ProviderReadinessState = 0 - ProviderReadinessState_PROVIDER_READINESS_STATE_PERSISTED ProviderReadinessState = 1 - ProviderReadinessState_PROVIDER_READINESS_STATE_PENDING ProviderReadinessState = 2 - ProviderReadinessState_PROVIDER_READINESS_STATE_READY ProviderReadinessState = 3 - ProviderReadinessState_PROVIDER_READINESS_STATE_WITHHELD ProviderReadinessState = 4 - ProviderReadinessState_PROVIDER_READINESS_STATE_REVOKED ProviderReadinessState = 5 - ProviderReadinessState_PROVIDER_READINESS_STATE_FAILED ProviderReadinessState = 6 - ProviderReadinessState_PROVIDER_READINESS_STATE_SUPERSEDED ProviderReadinessState = 7 -) - -// Enum value maps for ProviderReadinessState. -var ( - ProviderReadinessState_name = map[int32]string{ - 0: "PROVIDER_READINESS_STATE_UNSPECIFIED", - 1: "PROVIDER_READINESS_STATE_PERSISTED", - 2: "PROVIDER_READINESS_STATE_PENDING", - 3: "PROVIDER_READINESS_STATE_READY", - 4: "PROVIDER_READINESS_STATE_WITHHELD", - 5: "PROVIDER_READINESS_STATE_REVOKED", - 6: "PROVIDER_READINESS_STATE_FAILED", - 7: "PROVIDER_READINESS_STATE_SUPERSEDED", - } - ProviderReadinessState_value = map[string]int32{ - "PROVIDER_READINESS_STATE_UNSPECIFIED": 0, - "PROVIDER_READINESS_STATE_PERSISTED": 1, - "PROVIDER_READINESS_STATE_PENDING": 2, - "PROVIDER_READINESS_STATE_READY": 3, - "PROVIDER_READINESS_STATE_WITHHELD": 4, - "PROVIDER_READINESS_STATE_REVOKED": 5, - "PROVIDER_READINESS_STATE_FAILED": 6, - "PROVIDER_READINESS_STATE_SUPERSEDED": 7, - } -) - -func (x ProviderReadinessState) Enum() *ProviderReadinessState { - p := new(ProviderReadinessState) - *p = x - return p -} - -func (x ProviderReadinessState) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ProviderReadinessState) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[3].Descriptor() -} - -func (ProviderReadinessState) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[3] -} - -func (x ProviderReadinessState) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ProviderReadinessState.Descriptor instead. -func (ProviderReadinessState) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{3} -} - -// Closed reason categories are safe to display. Raw installation errors are -// never part of the readiness protocol. -type ProviderReadinessReason int32 - -const ( - ProviderReadinessReason_PROVIDER_READINESS_REASON_UNSPECIFIED ProviderReadinessReason = 0 - ProviderReadinessReason_PROVIDER_READINESS_REASON_WAITING_FOR_SUPERVISOR ProviderReadinessReason = 1 - ProviderReadinessReason_PROVIDER_READINESS_REASON_WAITING_FOR_CREDENTIALS ProviderReadinessReason = 2 - ProviderReadinessReason_PROVIDER_READINESS_REASON_WAITING_FOR_POLICY ProviderReadinessReason = 3 - ProviderReadinessReason_PROVIDER_READINESS_REASON_WAITING_FOR_PROCESS ProviderReadinessReason = 4 - ProviderReadinessReason_PROVIDER_READINESS_REASON_UNSUPPORTED_SUPERVISOR ProviderReadinessReason = 5 - ProviderReadinessReason_PROVIDER_READINESS_REASON_CREDENTIALS_WITHHELD ProviderReadinessReason = 6 - ProviderReadinessReason_PROVIDER_READINESS_REASON_CREDENTIAL_INSTALL_FAILED ProviderReadinessReason = 7 - ProviderReadinessReason_PROVIDER_READINESS_REASON_POLICY_ACTIVATION_FAILED ProviderReadinessReason = 8 - ProviderReadinessReason_PROVIDER_READINESS_REASON_PROCESS_INSTALL_FAILED ProviderReadinessReason = 9 - ProviderReadinessReason_PROVIDER_READINESS_REASON_SUPERVISOR_DISCONNECTED ProviderReadinessReason = 10 - ProviderReadinessReason_PROVIDER_READINESS_REASON_SUPERVISOR_LEASE_EXPIRED ProviderReadinessReason = 11 - ProviderReadinessReason_PROVIDER_READINESS_REASON_DESIRED_STATE_CHANGED ProviderReadinessReason = 12 - ProviderReadinessReason_PROVIDER_READINESS_REASON_CREDENTIAL_EXPIRED ProviderReadinessReason = 13 - ProviderReadinessReason_PROVIDER_READINESS_REASON_LOCAL_POLICY ProviderReadinessReason = 14 - ProviderReadinessReason_PROVIDER_READINESS_REASON_SNAPSHOT_MISMATCH ProviderReadinessReason = 15 -) - -// Enum value maps for ProviderReadinessReason. -var ( - ProviderReadinessReason_name = map[int32]string{ - 0: "PROVIDER_READINESS_REASON_UNSPECIFIED", - 1: "PROVIDER_READINESS_REASON_WAITING_FOR_SUPERVISOR", - 2: "PROVIDER_READINESS_REASON_WAITING_FOR_CREDENTIALS", - 3: "PROVIDER_READINESS_REASON_WAITING_FOR_POLICY", - 4: "PROVIDER_READINESS_REASON_WAITING_FOR_PROCESS", - 5: "PROVIDER_READINESS_REASON_UNSUPPORTED_SUPERVISOR", - 6: "PROVIDER_READINESS_REASON_CREDENTIALS_WITHHELD", - 7: "PROVIDER_READINESS_REASON_CREDENTIAL_INSTALL_FAILED", - 8: "PROVIDER_READINESS_REASON_POLICY_ACTIVATION_FAILED", - 9: "PROVIDER_READINESS_REASON_PROCESS_INSTALL_FAILED", - 10: "PROVIDER_READINESS_REASON_SUPERVISOR_DISCONNECTED", - 11: "PROVIDER_READINESS_REASON_SUPERVISOR_LEASE_EXPIRED", - 12: "PROVIDER_READINESS_REASON_DESIRED_STATE_CHANGED", - 13: "PROVIDER_READINESS_REASON_CREDENTIAL_EXPIRED", - 14: "PROVIDER_READINESS_REASON_LOCAL_POLICY", - 15: "PROVIDER_READINESS_REASON_SNAPSHOT_MISMATCH", - } - ProviderReadinessReason_value = map[string]int32{ - "PROVIDER_READINESS_REASON_UNSPECIFIED": 0, - "PROVIDER_READINESS_REASON_WAITING_FOR_SUPERVISOR": 1, - "PROVIDER_READINESS_REASON_WAITING_FOR_CREDENTIALS": 2, - "PROVIDER_READINESS_REASON_WAITING_FOR_POLICY": 3, - "PROVIDER_READINESS_REASON_WAITING_FOR_PROCESS": 4, - "PROVIDER_READINESS_REASON_UNSUPPORTED_SUPERVISOR": 5, - "PROVIDER_READINESS_REASON_CREDENTIALS_WITHHELD": 6, - "PROVIDER_READINESS_REASON_CREDENTIAL_INSTALL_FAILED": 7, - "PROVIDER_READINESS_REASON_POLICY_ACTIVATION_FAILED": 8, - "PROVIDER_READINESS_REASON_PROCESS_INSTALL_FAILED": 9, - "PROVIDER_READINESS_REASON_SUPERVISOR_DISCONNECTED": 10, - "PROVIDER_READINESS_REASON_SUPERVISOR_LEASE_EXPIRED": 11, - "PROVIDER_READINESS_REASON_DESIRED_STATE_CHANGED": 12, - "PROVIDER_READINESS_REASON_CREDENTIAL_EXPIRED": 13, - "PROVIDER_READINESS_REASON_LOCAL_POLICY": 14, - "PROVIDER_READINESS_REASON_SNAPSHOT_MISMATCH": 15, - } -) - -func (x ProviderReadinessReason) Enum() *ProviderReadinessReason { - p := new(ProviderReadinessReason) - *p = x - return p -} - -func (x ProviderReadinessReason) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ProviderReadinessReason) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[4].Descriptor() -} - -func (ProviderReadinessReason) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[4] -} - -func (x ProviderReadinessReason) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ProviderReadinessReason.Descriptor instead. -func (ProviderReadinessReason) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{4} -} - -// Component whose desired state is tracked by a durable update operation. -type ConfigComponent int32 - -const ( - ConfigComponent_CONFIG_COMPONENT_UNSPECIFIED ConfigComponent = 0 - ConfigComponent_CONFIG_COMPONENT_SANDBOX_CONFIG ConfigComponent = 1 - ConfigComponent_CONFIG_COMPONENT_PROVIDER_ENVIRONMENT ConfigComponent = 2 -) - -// Enum value maps for ConfigComponent. -var ( - ConfigComponent_name = map[int32]string{ - 0: "CONFIG_COMPONENT_UNSPECIFIED", - 1: "CONFIG_COMPONENT_SANDBOX_CONFIG", - 2: "CONFIG_COMPONENT_PROVIDER_ENVIRONMENT", - } - ConfigComponent_value = map[string]int32{ - "CONFIG_COMPONENT_UNSPECIFIED": 0, - "CONFIG_COMPONENT_SANDBOX_CONFIG": 1, - "CONFIG_COMPONENT_PROVIDER_ENVIRONMENT": 2, - } -) - -func (x ConfigComponent) Enum() *ConfigComponent { - p := new(ConfigComponent) - *p = x - return p -} - -func (x ConfigComponent) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ConfigComponent) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[5].Descriptor() -} - -func (ConfigComponent) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[5] -} - -func (x ConfigComponent) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ConfigComponent.Descriptor instead. -func (ConfigComponent) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{5} -} - -// Result of applying a component revision at its owning runtime boundary. -type ConfigApplyOutcome int32 - -const ( - ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_UNSPECIFIED ConfigApplyOutcome = 0 - ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_APPLIED ConfigApplyOutcome = 1 - ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_IGNORED_DUPLICATE ConfigApplyOutcome = 2 - ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_IGNORED_STALE ConfigApplyOutcome = 3 - ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_RETAINED_LOCAL_OVERRIDE ConfigApplyOutcome = 4 - ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_DEGRADED ConfigApplyOutcome = 5 - ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_FAILED_RETAINED_LAST_KNOWN_GOOD ConfigApplyOutcome = 6 - ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_FAILED_CLOSED ConfigApplyOutcome = 7 - ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_UNSUPPORTED ConfigApplyOutcome = 8 -) - -// Enum value maps for ConfigApplyOutcome. -var ( - ConfigApplyOutcome_name = map[int32]string{ - 0: "CONFIG_APPLY_OUTCOME_UNSPECIFIED", - 1: "CONFIG_APPLY_OUTCOME_APPLIED", - 2: "CONFIG_APPLY_OUTCOME_IGNORED_DUPLICATE", - 3: "CONFIG_APPLY_OUTCOME_IGNORED_STALE", - 4: "CONFIG_APPLY_OUTCOME_RETAINED_LOCAL_OVERRIDE", - 5: "CONFIG_APPLY_OUTCOME_DEGRADED", - 6: "CONFIG_APPLY_OUTCOME_FAILED_RETAINED_LAST_KNOWN_GOOD", - 7: "CONFIG_APPLY_OUTCOME_FAILED_CLOSED", - 8: "CONFIG_APPLY_OUTCOME_UNSUPPORTED", - } - ConfigApplyOutcome_value = map[string]int32{ - "CONFIG_APPLY_OUTCOME_UNSPECIFIED": 0, - "CONFIG_APPLY_OUTCOME_APPLIED": 1, - "CONFIG_APPLY_OUTCOME_IGNORED_DUPLICATE": 2, - "CONFIG_APPLY_OUTCOME_IGNORED_STALE": 3, - "CONFIG_APPLY_OUTCOME_RETAINED_LOCAL_OVERRIDE": 4, - "CONFIG_APPLY_OUTCOME_DEGRADED": 5, - "CONFIG_APPLY_OUTCOME_FAILED_RETAINED_LAST_KNOWN_GOOD": 6, - "CONFIG_APPLY_OUTCOME_FAILED_CLOSED": 7, - "CONFIG_APPLY_OUTCOME_UNSUPPORTED": 8, - } -) - -func (x ConfigApplyOutcome) Enum() *ConfigApplyOutcome { - p := new(ConfigApplyOutcome) - *p = x - return p -} - -func (x ConfigApplyOutcome) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ConfigApplyOutcome) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[6].Descriptor() -} - -func (ConfigApplyOutcome) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[6] -} - -func (x ConfigApplyOutcome) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ConfigApplyOutcome.Descriptor instead. -func (ConfigApplyOutcome) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{6} -} - -// Durable lifecycle of one desired-state update operation. -type ConfigUpdateOperationState int32 - -const ( - ConfigUpdateOperationState_CONFIG_UPDATE_OPERATION_STATE_UNSPECIFIED ConfigUpdateOperationState = 0 - ConfigUpdateOperationState_CONFIG_UPDATE_OPERATION_STATE_PENDING ConfigUpdateOperationState = 1 - ConfigUpdateOperationState_CONFIG_UPDATE_OPERATION_STATE_APPLIED ConfigUpdateOperationState = 2 - ConfigUpdateOperationState_CONFIG_UPDATE_OPERATION_STATE_INACTIVE ConfigUpdateOperationState = 3 - ConfigUpdateOperationState_CONFIG_UPDATE_OPERATION_STATE_FAILED ConfigUpdateOperationState = 4 - ConfigUpdateOperationState_CONFIG_UPDATE_OPERATION_STATE_SUPERSEDED ConfigUpdateOperationState = 5 - ConfigUpdateOperationState_CONFIG_UPDATE_OPERATION_STATE_CANCELLED ConfigUpdateOperationState = 6 -) - -// Enum value maps for ConfigUpdateOperationState. -var ( - ConfigUpdateOperationState_name = map[int32]string{ - 0: "CONFIG_UPDATE_OPERATION_STATE_UNSPECIFIED", - 1: "CONFIG_UPDATE_OPERATION_STATE_PENDING", - 2: "CONFIG_UPDATE_OPERATION_STATE_APPLIED", - 3: "CONFIG_UPDATE_OPERATION_STATE_INACTIVE", - 4: "CONFIG_UPDATE_OPERATION_STATE_FAILED", - 5: "CONFIG_UPDATE_OPERATION_STATE_SUPERSEDED", - 6: "CONFIG_UPDATE_OPERATION_STATE_CANCELLED", - } - ConfigUpdateOperationState_value = map[string]int32{ - "CONFIG_UPDATE_OPERATION_STATE_UNSPECIFIED": 0, - "CONFIG_UPDATE_OPERATION_STATE_PENDING": 1, - "CONFIG_UPDATE_OPERATION_STATE_APPLIED": 2, - "CONFIG_UPDATE_OPERATION_STATE_INACTIVE": 3, - "CONFIG_UPDATE_OPERATION_STATE_FAILED": 4, - "CONFIG_UPDATE_OPERATION_STATE_SUPERSEDED": 5, - "CONFIG_UPDATE_OPERATION_STATE_CANCELLED": 6, - } -) - -func (x ConfigUpdateOperationState) Enum() *ConfigUpdateOperationState { - p := new(ConfigUpdateOperationState) - *p = x - return p -} - -func (x ConfigUpdateOperationState) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ConfigUpdateOperationState) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[7].Descriptor() -} - -func (ConfigUpdateOperationState) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[7] -} - -func (x ConfigUpdateOperationState) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ConfigUpdateOperationState.Descriptor instead. -func (ConfigUpdateOperationState) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{7} -} - -// Provider credential token grant configuration. -// When present, the credential is obtained dynamically via OAuth2 grant when needed. -type ProviderCredentialTokenGrantType int32 - -const ( - ProviderCredentialTokenGrantType_PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_UNSPECIFIED ProviderCredentialTokenGrantType = 0 - ProviderCredentialTokenGrantType_PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_CLIENT_CREDENTIALS ProviderCredentialTokenGrantType = 1 - ProviderCredentialTokenGrantType_PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_TOKEN_EXCHANGE ProviderCredentialTokenGrantType = 2 -) - -// Enum value maps for ProviderCredentialTokenGrantType. -var ( - ProviderCredentialTokenGrantType_name = map[int32]string{ - 0: "PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_UNSPECIFIED", - 1: "PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_CLIENT_CREDENTIALS", - 2: "PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_TOKEN_EXCHANGE", - } - ProviderCredentialTokenGrantType_value = map[string]int32{ - "PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_UNSPECIFIED": 0, - "PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_CLIENT_CREDENTIALS": 1, - "PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_TOKEN_EXCHANGE": 2, - } -) - -func (x ProviderCredentialTokenGrantType) Enum() *ProviderCredentialTokenGrantType { - p := new(ProviderCredentialTokenGrantType) - *p = x - return p -} - -func (x ProviderCredentialTokenGrantType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ProviderCredentialTokenGrantType) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[8].Descriptor() -} - -func (ProviderCredentialTokenGrantType) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[8] -} - -func (x ProviderCredentialTokenGrantType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ProviderCredentialTokenGrantType.Descriptor instead. -func (ProviderCredentialTokenGrantType) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{8} -} - -type ProviderCredentialRefreshStrategy int32 - -const ( - ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED ProviderCredentialRefreshStrategy = 0 - ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_STATIC ProviderCredentialRefreshStrategy = 1 - ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_EXTERNAL ProviderCredentialRefreshStrategy = 2 - ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN ProviderCredentialRefreshStrategy = 3 - ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_CLIENT_CREDENTIALS ProviderCredentialRefreshStrategy = 4 - ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_GOOGLE_SERVICE_ACCOUNT_JWT ProviderCredentialRefreshStrategy = 5 - ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_AWS_STS_ASSUME_ROLE ProviderCredentialRefreshStrategy = 6 -) - -// Enum value maps for ProviderCredentialRefreshStrategy. -var ( - ProviderCredentialRefreshStrategy_name = map[int32]string{ - 0: "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED", - 1: "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_STATIC", - 2: "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_EXTERNAL", - 3: "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN", - 4: "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_CLIENT_CREDENTIALS", - 5: "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_GOOGLE_SERVICE_ACCOUNT_JWT", - 6: "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_AWS_STS_ASSUME_ROLE", - } - ProviderCredentialRefreshStrategy_value = map[string]int32{ - "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED": 0, - "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_STATIC": 1, - "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_EXTERNAL": 2, - "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN": 3, - "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_CLIENT_CREDENTIALS": 4, - "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_GOOGLE_SERVICE_ACCOUNT_JWT": 5, - "PROVIDER_CREDENTIAL_REFRESH_STRATEGY_AWS_STS_ASSUME_ROLE": 6, - } -) - -func (x ProviderCredentialRefreshStrategy) Enum() *ProviderCredentialRefreshStrategy { - p := new(ProviderCredentialRefreshStrategy) - *p = x - return p -} - -func (x ProviderCredentialRefreshStrategy) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ProviderCredentialRefreshStrategy) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[9].Descriptor() -} - -func (ProviderCredentialRefreshStrategy) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[9] -} - -func (x ProviderCredentialRefreshStrategy) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ProviderCredentialRefreshStrategy.Descriptor instead. -func (ProviderCredentialRefreshStrategy) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{9} -} - -// Stable provider profile categories used by clients for grouping and filtering. -type ProviderProfileCategory int32 - -const ( - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_UNSPECIFIED ProviderProfileCategory = 0 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_OTHER ProviderProfileCategory = 1 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE ProviderProfileCategory = 2 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_AGENT ProviderProfileCategory = 3 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL ProviderProfileCategory = 4 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_MESSAGING ProviderProfileCategory = 5 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_DATA ProviderProfileCategory = 6 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_KNOWLEDGE ProviderProfileCategory = 7 -) - -// Enum value maps for ProviderProfileCategory. -var ( - ProviderProfileCategory_name = map[int32]string{ - 0: "PROVIDER_PROFILE_CATEGORY_UNSPECIFIED", - 1: "PROVIDER_PROFILE_CATEGORY_OTHER", - 2: "PROVIDER_PROFILE_CATEGORY_INFERENCE", - 3: "PROVIDER_PROFILE_CATEGORY_AGENT", - 4: "PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL", - 5: "PROVIDER_PROFILE_CATEGORY_MESSAGING", - 6: "PROVIDER_PROFILE_CATEGORY_DATA", - 7: "PROVIDER_PROFILE_CATEGORY_KNOWLEDGE", - } - ProviderProfileCategory_value = map[string]int32{ - "PROVIDER_PROFILE_CATEGORY_UNSPECIFIED": 0, - "PROVIDER_PROFILE_CATEGORY_OTHER": 1, - "PROVIDER_PROFILE_CATEGORY_INFERENCE": 2, - "PROVIDER_PROFILE_CATEGORY_AGENT": 3, - "PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL": 4, - "PROVIDER_PROFILE_CATEGORY_MESSAGING": 5, - "PROVIDER_PROFILE_CATEGORY_DATA": 6, - "PROVIDER_PROFILE_CATEGORY_KNOWLEDGE": 7, - } -) - -func (x ProviderProfileCategory) Enum() *ProviderProfileCategory { - p := new(ProviderProfileCategory) - *p = x - return p -} - -func (x ProviderProfileCategory) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ProviderProfileCategory) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[10].Descriptor() -} - -func (ProviderProfileCategory) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[10] -} - -func (x ProviderProfileCategory) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ProviderProfileCategory.Descriptor instead. -func (ProviderProfileCategory) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{10} -} - -// Receivers treat UNSPECIFIED and unknown values as secret. -type ProviderEnvironmentValueClassification int32 - -const ( - ProviderEnvironmentValueClassification_PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_UNSPECIFIED ProviderEnvironmentValueClassification = 0 - ProviderEnvironmentValueClassification_PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_NON_SECRET ProviderEnvironmentValueClassification = 1 - ProviderEnvironmentValueClassification_PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_STATIC_CREDENTIAL ProviderEnvironmentValueClassification = 2 -) - -// Enum value maps for ProviderEnvironmentValueClassification. -var ( - ProviderEnvironmentValueClassification_name = map[int32]string{ - 0: "PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_UNSPECIFIED", - 1: "PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_NON_SECRET", - 2: "PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_STATIC_CREDENTIAL", - } - ProviderEnvironmentValueClassification_value = map[string]int32{ - "PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_UNSPECIFIED": 0, - "PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_NON_SECRET": 1, - "PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_STATIC_CREDENTIAL": 2, - } -) - -func (x ProviderEnvironmentValueClassification) Enum() *ProviderEnvironmentValueClassification { - p := new(ProviderEnvironmentValueClassification) - *p = x - return p -} - -func (x ProviderEnvironmentValueClassification) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ProviderEnvironmentValueClassification) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[11].Descriptor() -} - -func (ProviderEnvironmentValueClassification) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[11] -} - -func (x ProviderEnvironmentValueClassification) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ProviderEnvironmentValueClassification.Descriptor instead. -func (ProviderEnvironmentValueClassification) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{11} -} - -type ConfigurationAdmissionState int32 - -const ( - ConfigurationAdmissionState_CONFIGURATION_ADMISSION_STATE_UNSPECIFIED ConfigurationAdmissionState = 0 - ConfigurationAdmissionState_CONFIGURATION_ADMISSION_STATE_PENDING ConfigurationAdmissionState = 1 - ConfigurationAdmissionState_CONFIGURATION_ADMISSION_STATE_ACCEPTED ConfigurationAdmissionState = 2 - ConfigurationAdmissionState_CONFIGURATION_ADMISSION_STATE_REJECTED ConfigurationAdmissionState = 3 -) - -// Enum value maps for ConfigurationAdmissionState. -var ( - ConfigurationAdmissionState_name = map[int32]string{ - 0: "CONFIGURATION_ADMISSION_STATE_UNSPECIFIED", - 1: "CONFIGURATION_ADMISSION_STATE_PENDING", - 2: "CONFIGURATION_ADMISSION_STATE_ACCEPTED", - 3: "CONFIGURATION_ADMISSION_STATE_REJECTED", - } - ConfigurationAdmissionState_value = map[string]int32{ - "CONFIGURATION_ADMISSION_STATE_UNSPECIFIED": 0, - "CONFIGURATION_ADMISSION_STATE_PENDING": 1, - "CONFIGURATION_ADMISSION_STATE_ACCEPTED": 2, - "CONFIGURATION_ADMISSION_STATE_REJECTED": 3, - } -) - -func (x ConfigurationAdmissionState) Enum() *ConfigurationAdmissionState { - p := new(ConfigurationAdmissionState) - *p = x - return p -} - -func (x ConfigurationAdmissionState) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ConfigurationAdmissionState) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[12].Descriptor() -} - -func (ConfigurationAdmissionState) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[12] -} - -func (x ConfigurationAdmissionState) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ConfigurationAdmissionState.Descriptor instead. -func (ConfigurationAdmissionState) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{12} -} - -// Policy load status. -type PolicyStatus int32 - -const ( - PolicyStatus_POLICY_STATUS_UNSPECIFIED PolicyStatus = 0 - // Server received the update; sandbox has not yet loaded it. - PolicyStatus_POLICY_STATUS_PENDING PolicyStatus = 1 - // Sandbox successfully applied this policy version. - PolicyStatus_POLICY_STATUS_LOADED PolicyStatus = 2 - // Sandbox attempted to apply but failed; LKG policy remains active. - // ListSandboxPolicies also uses FAILED for historical payloads that are - // invalid under the current schema; load_error contains the diagnostic. - PolicyStatus_POLICY_STATUS_FAILED PolicyStatus = 3 - // A newer version was persisted before the sandbox loaded this one. - PolicyStatus_POLICY_STATUS_SUPERSEDED PolicyStatus = 4 -) - -// Enum value maps for PolicyStatus. -var ( - PolicyStatus_name = map[int32]string{ - 0: "POLICY_STATUS_UNSPECIFIED", - 1: "POLICY_STATUS_PENDING", - 2: "POLICY_STATUS_LOADED", - 3: "POLICY_STATUS_FAILED", - 4: "POLICY_STATUS_SUPERSEDED", - } - PolicyStatus_value = map[string]int32{ - "POLICY_STATUS_UNSPECIFIED": 0, - "POLICY_STATUS_PENDING": 1, - "POLICY_STATUS_LOADED": 2, - "POLICY_STATUS_FAILED": 3, - "POLICY_STATUS_SUPERSEDED": 4, - } -) - -func (x PolicyStatus) Enum() *PolicyStatus { - p := new(PolicyStatus) - *p = x - return p -} - -func (x PolicyStatus) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (PolicyStatus) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[13].Descriptor() -} - -func (PolicyStatus) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[13] -} - -func (x PolicyStatus) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use PolicyStatus.Descriptor instead. -func (PolicyStatus) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{13} -} - -// Service status enum. -type ServiceStatus int32 - -const ( - ServiceStatus_SERVICE_STATUS_UNSPECIFIED ServiceStatus = 0 - ServiceStatus_SERVICE_STATUS_HEALTHY ServiceStatus = 1 - ServiceStatus_SERVICE_STATUS_DEGRADED ServiceStatus = 2 - ServiceStatus_SERVICE_STATUS_UNHEALTHY ServiceStatus = 3 -) - -// Enum value maps for ServiceStatus. -var ( - ServiceStatus_name = map[int32]string{ - 0: "SERVICE_STATUS_UNSPECIFIED", - 1: "SERVICE_STATUS_HEALTHY", - 2: "SERVICE_STATUS_DEGRADED", - 3: "SERVICE_STATUS_UNHEALTHY", - } - ServiceStatus_value = map[string]int32{ - "SERVICE_STATUS_UNSPECIFIED": 0, - "SERVICE_STATUS_HEALTHY": 1, - "SERVICE_STATUS_DEGRADED": 2, - "SERVICE_STATUS_UNHEALTHY": 3, - } -) - -func (x ServiceStatus) Enum() *ServiceStatus { - p := new(ServiceStatus) - *p = x - return p -} - -func (x ServiceStatus) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ServiceStatus) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[14].Descriptor() -} - -func (ServiceStatus) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[14] -} - -func (x ServiceStatus) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ServiceStatus.Descriptor instead. -func (ServiceStatus) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{14} -} - -// Workspace-scoped role for members. -type WorkspaceRole int32 - -const ( - WorkspaceRole_WORKSPACE_ROLE_UNSPECIFIED WorkspaceRole = 0 - WorkspaceRole_WORKSPACE_ROLE_USER WorkspaceRole = 1 - WorkspaceRole_WORKSPACE_ROLE_ADMIN WorkspaceRole = 2 -) - -// Enum value maps for WorkspaceRole. -var ( - WorkspaceRole_name = map[int32]string{ - 0: "WORKSPACE_ROLE_UNSPECIFIED", - 1: "WORKSPACE_ROLE_USER", - 2: "WORKSPACE_ROLE_ADMIN", - } - WorkspaceRole_value = map[string]int32{ - "WORKSPACE_ROLE_UNSPECIFIED": 0, - "WORKSPACE_ROLE_USER": 1, - "WORKSPACE_ROLE_ADMIN": 2, - } -) - -func (x WorkspaceRole) Enum() *WorkspaceRole { - p := new(WorkspaceRole) - *p = x - return p -} - -func (x WorkspaceRole) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (WorkspaceRole) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[15].Descriptor() -} - -func (WorkspaceRole) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[15] -} - -func (x WorkspaceRole) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use WorkspaceRole.Descriptor instead. -func (WorkspaceRole) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{15} -} - -// Stable recovery action for the most recent provider credential refresh -// failure. Kept after the pre-existing enums so adding it does not renumber -// their generated descriptors. Clients should use this field instead of -// parsing last_error. -type ProviderCredentialRefreshRecoveryAction int32 - -const ( - ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED ProviderCredentialRefreshRecoveryAction = 0 - ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY ProviderCredentialRefreshRecoveryAction = 1 - ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE ProviderCredentialRefreshRecoveryAction = 2 - ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION ProviderCredentialRefreshRecoveryAction = 3 - ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE ProviderCredentialRefreshRecoveryAction = 4 -) - -// Enum value maps for ProviderCredentialRefreshRecoveryAction. -var ( - ProviderCredentialRefreshRecoveryAction_name = map[int32]string{ - 0: "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED", - 1: "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY", - 2: "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE", - 3: "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION", - 4: "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE", - } - ProviderCredentialRefreshRecoveryAction_value = map[string]int32{ - "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED": 0, - "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY": 1, - "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE": 2, - "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION": 3, - "PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE": 4, - } -) - -func (x ProviderCredentialRefreshRecoveryAction) Enum() *ProviderCredentialRefreshRecoveryAction { - p := new(ProviderCredentialRefreshRecoveryAction) - *p = x - return p -} - -func (x ProviderCredentialRefreshRecoveryAction) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ProviderCredentialRefreshRecoveryAction) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[16].Descriptor() -} - -func (ProviderCredentialRefreshRecoveryAction) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[16] -} - -func (x ProviderCredentialRefreshRecoveryAction) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ProviderCredentialRefreshRecoveryAction.Descriptor instead. -func (ProviderCredentialRefreshRecoveryAction) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{16} -} - -// Policy applied when the canonical main process exits outside an intentional -// stop or delete operation. Kept after existing enums so generated enum -// descriptors remain stable. -type SandboxRestartPolicy int32 - -const ( - SandboxRestartPolicy_SANDBOX_RESTART_POLICY_UNSPECIFIED SandboxRestartPolicy = 0 - SandboxRestartPolicy_SANDBOX_RESTART_POLICY_NEVER SandboxRestartPolicy = 1 - SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ON_FAILURE SandboxRestartPolicy = 2 - SandboxRestartPolicy_SANDBOX_RESTART_POLICY_ALWAYS SandboxRestartPolicy = 3 -) - -// Enum value maps for SandboxRestartPolicy. -var ( - SandboxRestartPolicy_name = map[int32]string{ - 0: "SANDBOX_RESTART_POLICY_UNSPECIFIED", - 1: "SANDBOX_RESTART_POLICY_NEVER", - 2: "SANDBOX_RESTART_POLICY_ON_FAILURE", - 3: "SANDBOX_RESTART_POLICY_ALWAYS", - } - SandboxRestartPolicy_value = map[string]int32{ - "SANDBOX_RESTART_POLICY_UNSPECIFIED": 0, - "SANDBOX_RESTART_POLICY_NEVER": 1, - "SANDBOX_RESTART_POLICY_ON_FAILURE": 2, - "SANDBOX_RESTART_POLICY_ALWAYS": 3, - } -) - -func (x SandboxRestartPolicy) Enum() *SandboxRestartPolicy { - p := new(SandboxRestartPolicy) - *p = x - return p -} - -func (x SandboxRestartPolicy) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (SandboxRestartPolicy) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[17].Descriptor() -} - -func (SandboxRestartPolicy) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[17] -} - -func (x SandboxRestartPolicy) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use SandboxRestartPolicy.Descriptor instead. -func (SandboxRestartPolicy) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{17} -} - -// Result of a public delete, membership removal, or session revocation. -// Default requests return NOT_FOUND for a missing target. With allow_missing, -// only a missing target becomes ALREADY_ABSENT; parent lookup, authorization, -// validation, precondition, and backend errors retain their normal status. -// These results describe the targeted resource, not a same-name replacement. -type DeletionOutcome int32 - -const ( - // No outcome was supplied. Never infer completion from this value. - DeletionOutcome_DELETION_OUTCOME_UNSPECIFIED DeletionOutcome = 0 - // The targeted gateway resource is removed (or the SSH session is revoked). - // Downstream platform garbage collection may still be finishing. - DeletionOutcome_DELETION_OUTCOME_COMPLETED DeletionOutcome = 1 - // Sandbox deletion is accepted but its gateway record still exists. - // Observe the targeted sandbox ID until it disappears for completion. - DeletionOutcome_DELETION_OUTCOME_ACCEPTED DeletionOutcome = 2 - // The target did not exist and allow_missing was true. - DeletionOutcome_DELETION_OUTCOME_ALREADY_ABSENT DeletionOutcome = 3 -) - -// Enum value maps for DeletionOutcome. -var ( - DeletionOutcome_name = map[int32]string{ - 0: "DELETION_OUTCOME_UNSPECIFIED", - 1: "DELETION_OUTCOME_COMPLETED", - 2: "DELETION_OUTCOME_ACCEPTED", - 3: "DELETION_OUTCOME_ALREADY_ABSENT", - } - DeletionOutcome_value = map[string]int32{ - "DELETION_OUTCOME_UNSPECIFIED": 0, - "DELETION_OUTCOME_COMPLETED": 1, - "DELETION_OUTCOME_ACCEPTED": 2, - "DELETION_OUTCOME_ALREADY_ABSENT": 3, - } -) - -func (x DeletionOutcome) Enum() *DeletionOutcome { - p := new(DeletionOutcome) - *p = x - return p -} - -func (x DeletionOutcome) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (DeletionOutcome) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[18].Descriptor() -} - -func (DeletionOutcome) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[18] -} - -func (x DeletionOutcome) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use DeletionOutcome.Descriptor instead. -func (DeletionOutcome) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{18} -} - -// Last observed network result for a configured external tool endpoint. -// Results describe accepted traffic observations, not present availability. -type EndpointResult int32 - -const ( - EndpointResult_ENDPOINT_RESULT_UNSPECIFIED EndpointResult = 0 - // No exchange has been observed under the current configuration and session. - EndpointResult_ENDPOINT_RESULT_NO_OBSERVED_EXCHANGE EndpointResult = 1 - // An upstream HTTP status below 400 was received. Its body can still contain - // an MCP error; this result does not establish tool-call success. - EndpointResult_ENDPOINT_RESULT_HTTP_RESPONSE_RECEIVED EndpointResult = 2 - // OpenShell policy denied the request locally. - EndpointResult_ENDPOINT_RESULT_POLICY_DENIED EndpointResult = 3 - // An applicable OpenShell-managed credential was unavailable. - EndpointResult_ENDPOINT_RESULT_CREDENTIAL_UNAVAILABLE EndpointResult = 4 - // TLS setup for the upstream connection failed. - EndpointResult_ENDPOINT_RESULT_TLS_FAILED EndpointResult = 5 - // The upstream transport failed before an HTTP response arrived. - EndpointResult_ENDPOINT_RESULT_TRANSPORT_FAILED EndpointResult = 6 - // The upstream service returned an HTTP rejection. - EndpointResult_ENDPOINT_RESULT_UPSTREAM_REJECTED EndpointResult = 7 -) - -// Enum value maps for EndpointResult. -var ( - EndpointResult_name = map[int32]string{ - 0: "ENDPOINT_RESULT_UNSPECIFIED", - 1: "ENDPOINT_RESULT_NO_OBSERVED_EXCHANGE", - 2: "ENDPOINT_RESULT_HTTP_RESPONSE_RECEIVED", - 3: "ENDPOINT_RESULT_POLICY_DENIED", - 4: "ENDPOINT_RESULT_CREDENTIAL_UNAVAILABLE", - 5: "ENDPOINT_RESULT_TLS_FAILED", - 6: "ENDPOINT_RESULT_TRANSPORT_FAILED", - 7: "ENDPOINT_RESULT_UPSTREAM_REJECTED", - } - EndpointResult_value = map[string]int32{ - "ENDPOINT_RESULT_UNSPECIFIED": 0, - "ENDPOINT_RESULT_NO_OBSERVED_EXCHANGE": 1, - "ENDPOINT_RESULT_HTTP_RESPONSE_RECEIVED": 2, - "ENDPOINT_RESULT_POLICY_DENIED": 3, - "ENDPOINT_RESULT_CREDENTIAL_UNAVAILABLE": 4, - "ENDPOINT_RESULT_TLS_FAILED": 5, - "ENDPOINT_RESULT_TRANSPORT_FAILED": 6, - "ENDPOINT_RESULT_UPSTREAM_REJECTED": 7, - } -) - -func (x EndpointResult) Enum() *EndpointResult { - p := new(EndpointResult) - *p = x - return p -} - -func (x EndpointResult) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (EndpointResult) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[19].Descriptor() -} - -func (EndpointResult) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[19] -} - -func (x EndpointResult) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use EndpointResult.Descriptor instead. -func (EndpointResult) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{19} -} - -// Controls whether an exposed service receives the request's application -// Authorization header. -type ServiceAuthorizationMode int32 - -const ( - // Omission preserves the secure legacy behavior and resolves to STRIP. - ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_UNSPECIFIED ServiceAuthorizationMode = 0 - // Remove Authorization before forwarding to the sandbox service. - ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_STRIP ServiceAuthorizationMode = 1 - // Forward one syntactically valid bearer Authorization header unchanged. - ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_BEARER_PASSTHROUGH ServiceAuthorizationMode = 2 -) - -// Enum value maps for ServiceAuthorizationMode. -var ( - ServiceAuthorizationMode_name = map[int32]string{ - 0: "SERVICE_AUTHORIZATION_MODE_UNSPECIFIED", - 1: "SERVICE_AUTHORIZATION_MODE_STRIP", - 2: "SERVICE_AUTHORIZATION_MODE_BEARER_PASSTHROUGH", - } - ServiceAuthorizationMode_value = map[string]int32{ - "SERVICE_AUTHORIZATION_MODE_UNSPECIFIED": 0, - "SERVICE_AUTHORIZATION_MODE_STRIP": 1, - "SERVICE_AUTHORIZATION_MODE_BEARER_PASSTHROUGH": 2, - } -) - -func (x ServiceAuthorizationMode) Enum() *ServiceAuthorizationMode { - p := new(ServiceAuthorizationMode) - *p = x - return p -} - -func (x ServiceAuthorizationMode) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ServiceAuthorizationMode) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[20].Descriptor() -} - -func (ServiceAuthorizationMode) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[20] -} - -func (x ServiceAuthorizationMode) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ServiceAuthorizationMode.Descriptor instead. -func (ServiceAuthorizationMode) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{20} -} - -// Subsystem whose closed code set explains an endpoint failure. Each domain -// defines its codes, stages, recovery actions, and the endpoint results it -// can explain. Kept at the end of the file so adding it does not renumber -// existing generated descriptors. -type EndpointFailureDomain int32 - -const ( - EndpointFailureDomain_ENDPOINT_FAILURE_DOMAIN_UNSPECIFIED EndpointFailureDomain = 0 - // A dynamic token grant for a provider credential failed, so OpenShell - // refused the request before any of it reached the endpoint. Explains - // ENDPOINT_RESULT_CREDENTIAL_UNAVAILABLE. - EndpointFailureDomain_ENDPOINT_FAILURE_DOMAIN_TOKEN_GRANT EndpointFailureDomain = 1 -) - -// Enum value maps for EndpointFailureDomain. -var ( - EndpointFailureDomain_name = map[int32]string{ - 0: "ENDPOINT_FAILURE_DOMAIN_UNSPECIFIED", - 1: "ENDPOINT_FAILURE_DOMAIN_TOKEN_GRANT", - } - EndpointFailureDomain_value = map[string]int32{ - "ENDPOINT_FAILURE_DOMAIN_UNSPECIFIED": 0, - "ENDPOINT_FAILURE_DOMAIN_TOKEN_GRANT": 1, - } -) - -func (x EndpointFailureDomain) Enum() *EndpointFailureDomain { - p := new(EndpointFailureDomain) - *p = x - return p -} - -func (x EndpointFailureDomain) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (EndpointFailureDomain) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[21].Descriptor() -} - -func (EndpointFailureDomain) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[21] -} - -func (x EndpointFailureDomain) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use EndpointFailureDomain.Descriptor instead. -func (EndpointFailureDomain) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{21} -} - -// IssueSandboxToken request. Empty body; identity is established by the -// authentication credentials carried in the request headers (a projected -// Kubernetes ServiceAccount JWT in the K8s driver path). -type IssueSandboxTokenRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *IssueSandboxTokenRequest) Reset() { - *x = IssueSandboxTokenRequest{} - mi := &file_openshell_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *IssueSandboxTokenRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*IssueSandboxTokenRequest) ProtoMessage() {} - -func (x *IssueSandboxTokenRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use IssueSandboxTokenRequest.ProtoReflect.Descriptor instead. -func (*IssueSandboxTokenRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{0} -} - -// IssueSandboxToken response. The supervisor caches the returned token in -// memory and presents it as `Authorization: Bearer` on every subsequent -// gateway RPC. -type IssueSandboxTokenResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Gateway-minted session JWT bound to the calling sandbox's UUID, active - // runtime generation, authorization epoch, and durable token lineage. - Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` - // Absolute expiry of the issued token. Absence means the token is non-expiring. - ExpirationTime *timestamppb.Timestamp `protobuf:"bytes,102,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *IssueSandboxTokenResponse) Reset() { - *x = IssueSandboxTokenResponse{} - mi := &file_openshell_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *IssueSandboxTokenResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*IssueSandboxTokenResponse) ProtoMessage() {} - -func (x *IssueSandboxTokenResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use IssueSandboxTokenResponse.ProtoReflect.Descriptor instead. -func (*IssueSandboxTokenResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{1} -} - -func (x *IssueSandboxTokenResponse) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -func (x *IssueSandboxTokenResponse) GetExpirationTime() *timestamppb.Timestamp { - if x != nil { - return x.ExpirationTime - } - return nil -} - -// RefreshSandboxToken request. The calling principal must already be a -// sandbox principal (i.e. the request carries a still-valid gateway-minted -// JWT in its Authorization header). Extension service names are resolved -// against server-owned registrations and the sandbox's effective policy; -// callers never choose token audiences directly. -type RefreshSandboxTokenRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Operator registration names for extension services selected by the - // sandbox's effective policy. - ExtensionServiceNames []string `protobuf:"bytes,1,rep,name=extension_service_names,json=extensionServiceNames,proto3" json:"extension_service_names,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RefreshSandboxTokenRequest) Reset() { - *x = RefreshSandboxTokenRequest{} - mi := &file_openshell_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RefreshSandboxTokenRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RefreshSandboxTokenRequest) ProtoMessage() {} - -func (x *RefreshSandboxTokenRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RefreshSandboxTokenRequest.ProtoReflect.Descriptor instead. -func (*RefreshSandboxTokenRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{2} -} - -func (x *RefreshSandboxTokenRequest) GetExtensionServiceNames() []string { - if x != nil { - return x.ExtensionServiceNames - } - return nil -} - -// RefreshSandboxToken response. The new token replaces the supervisor's -// in-memory bearer credential. -type RefreshSandboxTokenResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Fresh gateway-minted JWT bound to the same sandbox UUID. - Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` - // Absolute expiry of the new token. Absence means the token is non-expiring. - ExpirationTime *timestamppb.Timestamp `protobuf:"bytes,102,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` - // Fresh credentials for the requested, policy-authorized extension - // services. These remain in supervisor memory and are never persisted. - ExtensionCredentials []*ExtensionServiceCredential `protobuf:"bytes,3,rep,name=extension_credentials,json=extensionCredentials,proto3" json:"extension_credentials,omitempty"` - // Fresh Sandbox Protocol bearer token from the same atomic refresh. - SandboxToken string `protobuf:"bytes,4,opt,name=sandbox_token,json=sandboxToken,proto3" json:"sandbox_token,omitempty"` - // Absolute Sandbox Protocol token expiry. Absence means the token is non-expiring. - SandboxExpirationTime *timestamppb.Timestamp `protobuf:"bytes,105,opt,name=sandbox_expiration_time,json=sandboxExpirationTime,proto3" json:"sandbox_expiration_time,omitempty"` - // Launch generation to which both refreshed credentials are bound. - SessionId string `protobuf:"bytes,6,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` - // Durable authorization epoch shared by the gateway and Sandbox Runtime. - CredentialEpoch uint64 `protobuf:"varint,7,opt,name=credential_epoch,json=credentialEpoch,proto3" json:"credential_epoch,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RefreshSandboxTokenResponse) Reset() { - *x = RefreshSandboxTokenResponse{} - mi := &file_openshell_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RefreshSandboxTokenResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RefreshSandboxTokenResponse) ProtoMessage() {} - -func (x *RefreshSandboxTokenResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RefreshSandboxTokenResponse.ProtoReflect.Descriptor instead. -func (*RefreshSandboxTokenResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{3} -} - -func (x *RefreshSandboxTokenResponse) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -func (x *RefreshSandboxTokenResponse) GetExpirationTime() *timestamppb.Timestamp { - if x != nil { - return x.ExpirationTime - } - return nil -} - -func (x *RefreshSandboxTokenResponse) GetExtensionCredentials() []*ExtensionServiceCredential { - if x != nil { - return x.ExtensionCredentials - } - return nil -} - -func (x *RefreshSandboxTokenResponse) GetSandboxToken() string { - if x != nil { - return x.SandboxToken - } - return "" -} - -func (x *RefreshSandboxTokenResponse) GetSandboxExpirationTime() *timestamppb.Timestamp { - if x != nil { - return x.SandboxExpirationTime - } - return nil -} - -func (x *RefreshSandboxTokenResponse) GetSessionId() string { - if x != nil { - return x.SessionId - } - return "" -} - -func (x *RefreshSandboxTokenResponse) GetCredentialEpoch() uint64 { - if x != nil { - return x.CredentialEpoch - } - return 0 -} - -// Health check request. -type HealthRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *HealthRequest) Reset() { - *x = HealthRequest{} - mi := &file_openshell_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *HealthRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HealthRequest) ProtoMessage() {} - -func (x *HealthRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HealthRequest.ProtoReflect.Descriptor instead. -func (*HealthRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{4} -} - -// Health check response. -type HealthResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Service status. - Status ServiceStatus `protobuf:"varint,1,opt,name=status,proto3,enum=openshell.v1.ServiceStatus" json:"status,omitempty"` - // Service version. - Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *HealthResponse) Reset() { - *x = HealthResponse{} - mi := &file_openshell_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *HealthResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HealthResponse) ProtoMessage() {} - -func (x *HealthResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HealthResponse.ProtoReflect.Descriptor instead. -func (*HealthResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{5} -} - -func (x *HealthResponse) GetStatus() ServiceStatus { - if x != nil { - return x.Status - } - return ServiceStatus_SERVICE_STATUS_UNSPECIFIED -} - -func (x *HealthResponse) GetVersion() string { - if x != nil { - return x.Version - } - return "" -} - -// Current-user request. The identity comes from the authenticated request. -type GetCurrentUserRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetCurrentUserRequest) Reset() { - *x = GetCurrentUserRequest{} - mi := &file_openshell_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetCurrentUserRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetCurrentUserRequest) ProtoMessage() {} - -func (x *GetCurrentUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[6] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetCurrentUserRequest.ProtoReflect.Descriptor instead. -func (*GetCurrentUserRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{6} -} - -// Authenticated user identity as validated by the gateway. -type GetCurrentUserResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Stable identity subject (for example, the OIDC `sub` claim). - Subject string `protobuf:"bytes,1,opt,name=subject,proto3" json:"subject,omitempty"` - // Human-readable identity name when supplied by the authentication provider. - DisplayName string `protobuf:"bytes,2,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` - // Roles granted to the authenticated identity. - Roles []string `protobuf:"bytes,3,rep,name=roles,proto3" json:"roles,omitempty"` - // OAuth2 scopes granted to the authenticated identity. - Scopes []string `protobuf:"bytes,4,rep,name=scopes,proto3" json:"scopes,omitempty"` - // Authentication provider that established the identity. - IdentityProvider string `protobuf:"bytes,5,opt,name=identity_provider,json=identityProvider,proto3" json:"identity_provider,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetCurrentUserResponse) Reset() { - *x = GetCurrentUserResponse{} - mi := &file_openshell_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetCurrentUserResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetCurrentUserResponse) ProtoMessage() {} - -func (x *GetCurrentUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[7] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetCurrentUserResponse.ProtoReflect.Descriptor instead. -func (*GetCurrentUserResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{7} -} - -func (x *GetCurrentUserResponse) GetSubject() string { - if x != nil { - return x.Subject - } - return "" -} - -func (x *GetCurrentUserResponse) GetDisplayName() string { - if x != nil { - return x.DisplayName - } - return "" -} - -func (x *GetCurrentUserResponse) GetRoles() []string { - if x != nil { - return x.Roles - } - return nil -} - -func (x *GetCurrentUserResponse) GetScopes() []string { - if x != nil { - return x.Scopes - } - return nil -} - -func (x *GetCurrentUserResponse) GetIdentityProvider() string { - if x != nil { - return x.IdentityProvider - } - return "" -} - -// Gateway info request. -type GetGatewayInfoRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetGatewayInfoRequest) Reset() { - *x = GetGatewayInfoRequest{} - mi := &file_openshell_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetGatewayInfoRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetGatewayInfoRequest) ProtoMessage() {} - -func (x *GetGatewayInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[8] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetGatewayInfoRequest.ProtoReflect.Descriptor instead. -func (*GetGatewayInfoRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{8} -} - -// Gateway info response. -type GetGatewayInfoResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Service status. - Status ServiceStatus `protobuf:"varint,1,opt,name=status,proto3,enum=openshell.v1.ServiceStatus" json:"status,omitempty"` - // OpenShell gateway binary version. - GatewayVersion string `protobuf:"bytes,2,opt,name=gateway_version,json=gatewayVersion,proto3" json:"gateway_version,omitempty"` - // Compute driver runtimes initialized by this gateway. Current gateways - // return exactly one entry. - ComputeDrivers []*ComputeDriverInfo `protobuf:"bytes,3,rep,name=compute_drivers,json=computeDrivers,proto3" json:"compute_drivers,omitempty"` - // Negotiated non-secret metadata for every initialized extension. - Extensions []*NegotiatedExtensionInfo `protobuf:"bytes,4,rep,name=extensions,proto3" json:"extensions,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetGatewayInfoResponse) Reset() { - *x = GetGatewayInfoResponse{} - mi := &file_openshell_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetGatewayInfoResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetGatewayInfoResponse) ProtoMessage() {} - -func (x *GetGatewayInfoResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[9] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetGatewayInfoResponse.ProtoReflect.Descriptor instead. -func (*GetGatewayInfoResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{9} -} - -func (x *GetGatewayInfoResponse) GetStatus() ServiceStatus { - if x != nil { - return x.Status - } - return ServiceStatus_SERVICE_STATUS_UNSPECIFIED -} - -func (x *GetGatewayInfoResponse) GetGatewayVersion() string { - if x != nil { - return x.GatewayVersion - } - return "" -} - -func (x *GetGatewayInfoResponse) GetComputeDrivers() []*ComputeDriverInfo { - if x != nil { - return x.ComputeDrivers - } - return nil -} - -func (x *GetGatewayInfoResponse) GetExtensions() []*NegotiatedExtensionInfo { - if x != nil { - return x.Extensions - } - return nil -} - -// Public, non-secret snapshot of one successful startup negotiation. -type NegotiatedExtensionInfo struct { - state protoimpl.MessageState `protogen:"open.v1"` - Kind ExtensionKind `protobuf:"varint,1,opt,name=kind,proto3,enum=openshell.v1.ExtensionKind" json:"kind,omitempty"` - // Gateway/operator-selected registration name. - ConfiguredName string `protobuf:"bytes,2,opt,name=configured_name,json=configuredName,proto3" json:"configured_name,omitempty"` - // Extension-reported implementation identity. - ImplementationName string `protobuf:"bytes,3,opt,name=implementation_name,json=implementationName,proto3" json:"implementation_name,omitempty"` - // Extension build version, distinct from the protocol version. - ImplementationVersion string `protobuf:"bytes,4,opt,name=implementation_version,json=implementationVersion,proto3" json:"implementation_version,omitempty"` - ProtocolMajor uint32 `protobuf:"varint,5,opt,name=protocol_major,json=protocolMajor,proto3" json:"protocol_major,omitempty"` - ProtocolMinor uint32 `protobuf:"varint,6,opt,name=protocol_minor,json=protocolMinor,proto3" json:"protocol_minor,omitempty"` - // Extension-supported optional capabilities, sorted for stable output. - SupportedCapabilities []string `protobuf:"bytes,7,rep,name=supported_capabilities,json=supportedCapabilities,proto3" json:"supported_capabilities,omitempty"` - // Capabilities the extension requires from the gateway. - RequiredCapabilities []string `protobuf:"bytes,8,rep,name=required_capabilities,json=requiredCapabilities,proto3" json:"required_capabilities,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *NegotiatedExtensionInfo) Reset() { - *x = NegotiatedExtensionInfo{} - mi := &file_openshell_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *NegotiatedExtensionInfo) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NegotiatedExtensionInfo) ProtoMessage() {} - -func (x *NegotiatedExtensionInfo) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[10] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NegotiatedExtensionInfo.ProtoReflect.Descriptor instead. -func (*NegotiatedExtensionInfo) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{10} -} - -func (x *NegotiatedExtensionInfo) GetKind() ExtensionKind { - if x != nil { - return x.Kind - } - return ExtensionKind_EXTENSION_KIND_UNSPECIFIED -} - -func (x *NegotiatedExtensionInfo) GetConfiguredName() string { - if x != nil { - return x.ConfiguredName - } - return "" -} - -func (x *NegotiatedExtensionInfo) GetImplementationName() string { - if x != nil { - return x.ImplementationName - } - return "" -} - -func (x *NegotiatedExtensionInfo) GetImplementationVersion() string { - if x != nil { - return x.ImplementationVersion - } - return "" -} - -func (x *NegotiatedExtensionInfo) GetProtocolMajor() uint32 { - if x != nil { - return x.ProtocolMajor - } - return 0 -} - -func (x *NegotiatedExtensionInfo) GetProtocolMinor() uint32 { - if x != nil { - return x.ProtocolMinor - } - return 0 -} - -func (x *NegotiatedExtensionInfo) GetSupportedCapabilities() []string { - if x != nil { - return x.SupportedCapabilities - } - return nil -} - -func (x *NegotiatedExtensionInfo) GetRequiredCapabilities() []string { - if x != nil { - return x.RequiredCapabilities - } - return nil -} - -// Info for one initialized compute driver runtime. -type ComputeDriverInfo struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Gateway-selected driver name used for routing and driver_config keys. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Capabilities reported by the driver during gateway runtime initialization. - Capabilities *ComputeDriverCapabilities `protobuf:"bytes,2,opt,name=capabilities,proto3" json:"capabilities,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ComputeDriverInfo) Reset() { - *x = ComputeDriverInfo{} - mi := &file_openshell_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ComputeDriverInfo) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ComputeDriverInfo) ProtoMessage() {} - -func (x *ComputeDriverInfo) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[11] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ComputeDriverInfo.ProtoReflect.Descriptor instead. -func (*ComputeDriverInfo) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{11} -} - -func (x *ComputeDriverInfo) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ComputeDriverInfo) GetCapabilities() *ComputeDriverCapabilities { - if x != nil { - return x.Capabilities - } - return nil -} - -// Public compute driver capability snapshot. -type ComputeDriverCapabilities struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Driver-reported human-readable name from the startup capability snapshot. - DriverName string `protobuf:"bytes,1,opt,name=driver_name,json=driverName,proto3" json:"driver_name,omitempty"` - // Driver-reported implementation version from the startup capability snapshot. - DriverVersion string `protobuf:"bytes,2,opt,name=driver_version,json=driverVersion,proto3" json:"driver_version,omitempty"` - // Static portable resource request forms reported by the driver. - ResourceCapabilities *ResourceCapabilities `protobuf:"bytes,3,opt,name=resource_capabilities,json=resourceCapabilities,proto3" json:"resource_capabilities,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ComputeDriverCapabilities) Reset() { - *x = ComputeDriverCapabilities{} - mi := &file_openshell_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ComputeDriverCapabilities) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ComputeDriverCapabilities) ProtoMessage() {} - -func (x *ComputeDriverCapabilities) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[12] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ComputeDriverCapabilities.ProtoReflect.Descriptor instead. -func (*ComputeDriverCapabilities) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{12} -} - -func (x *ComputeDriverCapabilities) GetDriverName() string { - if x != nil { - return x.DriverName - } - return "" -} - -func (x *ComputeDriverCapabilities) GetDriverVersion() string { - if x != nil { - return x.DriverVersion - } - return "" -} - -func (x *ComputeDriverCapabilities) GetResourceCapabilities() *ResourceCapabilities { - if x != nil { - return x.ResourceCapabilities - } - return nil -} - -// Static portable resource request forms reported by a compute driver. -// An omitted domain means the driver does not report that domain. -type ResourceCapabilities struct { - state protoimpl.MessageState `protogen:"open.v1"` - Cpu *CpuResourceCapabilities `protobuf:"bytes,1,opt,name=cpu,proto3" json:"cpu,omitempty"` - Memory *MemoryResourceCapabilities `protobuf:"bytes,2,opt,name=memory,proto3" json:"memory,omitempty"` - Gpu *GpuResourceCapabilities `protobuf:"bytes,3,opt,name=gpu,proto3" json:"gpu,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ResourceCapabilities) Reset() { - *x = ResourceCapabilities{} - mi := &file_openshell_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ResourceCapabilities) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ResourceCapabilities) ProtoMessage() {} - -func (x *ResourceCapabilities) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[13] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ResourceCapabilities.ProtoReflect.Descriptor instead. -func (*ResourceCapabilities) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{13} -} - -func (x *ResourceCapabilities) GetCpu() *CpuResourceCapabilities { - if x != nil { - return x.Cpu - } - return nil -} - -func (x *ResourceCapabilities) GetMemory() *MemoryResourceCapabilities { - if x != nil { - return x.Memory - } - return nil -} - -func (x *ResourceCapabilities) GetGpu() *GpuResourceCapabilities { - if x != nil { - return x.Gpu - } - return nil -} - -type CpuResourceCapabilities struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The driver accepts and enforces a portable CPU limit. - LimitSupported bool `protobuf:"varint,1,opt,name=limit_supported,json=limitSupported,proto3" json:"limit_supported,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CpuResourceCapabilities) Reset() { - *x = CpuResourceCapabilities{} - mi := &file_openshell_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CpuResourceCapabilities) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CpuResourceCapabilities) ProtoMessage() {} - -func (x *CpuResourceCapabilities) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[14] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CpuResourceCapabilities.ProtoReflect.Descriptor instead. -func (*CpuResourceCapabilities) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{14} -} - -func (x *CpuResourceCapabilities) GetLimitSupported() bool { - if x != nil { - return x.LimitSupported - } - return false -} - -type MemoryResourceCapabilities struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The driver accepts and enforces a portable memory limit. - LimitSupported bool `protobuf:"varint,1,opt,name=limit_supported,json=limitSupported,proto3" json:"limit_supported,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *MemoryResourceCapabilities) Reset() { - *x = MemoryResourceCapabilities{} - mi := &file_openshell_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *MemoryResourceCapabilities) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MemoryResourceCapabilities) ProtoMessage() {} - -func (x *MemoryResourceCapabilities) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[15] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use MemoryResourceCapabilities.ProtoReflect.Descriptor instead. -func (*MemoryResourceCapabilities) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{15} -} - -func (x *MemoryResourceCapabilities) GetLimitSupported() bool { - if x != nil { - return x.LimitSupported - } - return false -} - -type GpuResourceCapabilities struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The driver accepts a GPU request with no explicit count. - DefaultSelectionSupported bool `protobuf:"varint,1,opt,name=default_selection_supported,json=defaultSelectionSupported,proto3" json:"default_selection_supported,omitempty"` - // The driver accepts an explicit `gpu.count` request. - CountSelectionSupported bool `protobuf:"varint,2,opt,name=count_selection_supported,json=countSelectionSupported,proto3" json:"count_selection_supported,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GpuResourceCapabilities) Reset() { - *x = GpuResourceCapabilities{} - mi := &file_openshell_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GpuResourceCapabilities) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GpuResourceCapabilities) ProtoMessage() {} - -func (x *GpuResourceCapabilities) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[16] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GpuResourceCapabilities.ProtoReflect.Descriptor instead. -func (*GpuResourceCapabilities) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{16} -} - -func (x *GpuResourceCapabilities) GetDefaultSelectionSupported() bool { - if x != nil { - return x.DefaultSelectionSupported - } - return false -} - -func (x *GpuResourceCapabilities) GetCountSelectionSupported() bool { - if x != nil { - return x.CountSelectionSupported - } - return false -} - -// Public sandbox resource exposed by the OpenShell API. -// -// This is the canonical gateway-owned view of a sandbox. It merges user intent -// (`spec`) with gateway-managed metadata and status derived from internal -// compute-driver observations. -// -// Note: The `namespace` field has been removed from the public API. It remains -// in the internal `DriverSandbox` message as a compute-driver implementation detail. -type Sandbox struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Kubernetes-style metadata (id, name, labels, timestamps, resource version). - Metadata *datamodelv1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - // Desired sandbox configuration submitted through the API. - Spec *SandboxSpec `protobuf:"bytes,2,opt,name=spec,proto3" json:"spec,omitempty"` - // Latest user-facing observed status derived by the gateway. - Status *SandboxStatus `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` - // Read-only SSH host identity in OpenSSH SHA256: form. Stable for - // this sandbox's lifetime, including runtime restarts. Empty on legacy - // runtimes that do not provide a gateway-managed SSH identity. - HostKeyFingerprint string `protobuf:"bytes,6,opt,name=host_key_fingerprint,json=hostKeyFingerprint,proto3" json:"host_key_fingerprint,omitempty"` - // Read-only provenance for sandboxes created from a reusable workload template. - CreatedFromWorkloadTemplate *SandboxWorkloadTemplateProvenance `protobuf:"bytes,20,opt,name=created_from_workload_template,json=createdFromWorkloadTemplate,proto3" json:"created_from_workload_template,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Sandbox) Reset() { - *x = Sandbox{} - mi := &file_openshell_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Sandbox) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Sandbox) ProtoMessage() {} - -func (x *Sandbox) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[17] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Sandbox.ProtoReflect.Descriptor instead. -func (*Sandbox) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{17} -} - -func (x *Sandbox) GetMetadata() *datamodelv1.ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *Sandbox) GetSpec() *SandboxSpec { - if x != nil { - return x.Spec - } - return nil -} - -func (x *Sandbox) GetStatus() *SandboxStatus { - if x != nil { - return x.Status - } - return nil -} - -func (x *Sandbox) GetHostKeyFingerprint() string { - if x != nil { - return x.HostKeyFingerprint - } - return "" -} - -func (x *Sandbox) GetCreatedFromWorkloadTemplate() *SandboxWorkloadTemplateProvenance { - if x != nil { - return x.CreatedFromWorkloadTemplate - } - return nil -} - -// Desired sandbox configuration provided through the public API. -type SandboxSpec struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Log level exposed to processes running inside the sandbox. - LogLevel string `protobuf:"bytes,1,opt,name=log_level,json=logLevel,proto3" json:"log_level,omitempty"` - // Environment variables injected into the sandbox runtime. - Environment map[string]string `protobuf:"bytes,5,rep,name=environment,proto3" json:"environment,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Container or VM template used to provision the sandbox. - Template *SandboxTemplate `protobuf:"bytes,6,opt,name=template,proto3" json:"template,omitempty"` - // Required sandbox policy configuration. - Policy *sandboxv1.SandboxPolicy `protobuf:"bytes,7,opt,name=policy,proto3" json:"policy,omitempty"` - // Provider names to attach to this sandbox. - Providers []string `protobuf:"bytes,8,rep,name=providers,proto3" json:"providers,omitempty"` - // Portable resource requirements used by the gateway for driver selection - // and by drivers for provisioning. - ResourceRequirements *ResourceRequirements `protobuf:"bytes,9,opt,name=resource_requirements,json=resourceRequirements,proto3" json:"resource_requirements,omitempty"` - // Canonical command launched once by the sandbox supervisor. No shell - // parsing is performed. The gateway normalizes an omitted command to the - // portable scratch login shell before persistence. - Command []string `protobuf:"bytes,12,rep,name=command,proto3" json:"command,omitempty"` - // Allocate a retained pseudo-terminal for the main process. - Tty bool `protobuf:"varint,13,opt,name=tty,proto3" json:"tty,omitempty"` - // Gateway-owned attachment identity, changed atomically with the provider set. - // Equality only: detach and reattach must not revive an older receipt. - ProviderAttachmentEpoch string `protobuf:"bytes,14,opt,name=provider_attachment_epoch,json=providerAttachmentEpoch,proto3" json:"provider_attachment_epoch,omitempty"` - // Gateway-owned policy for replacing the sandbox runtime after the canonical - // main process exits. Unspecified is normalized to Never before persistence. - RestartPolicy SandboxRestartPolicy `protobuf:"varint,15,opt,name=restart_policy,json=restartPolicy,proto3,enum=openshell.v1.SandboxRestartPolicy" json:"restart_policy,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxSpec) Reset() { - *x = SandboxSpec{} - mi := &file_openshell_proto_msgTypes[18] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxSpec) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxSpec) ProtoMessage() {} - -func (x *SandboxSpec) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[18] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxSpec.ProtoReflect.Descriptor instead. -func (*SandboxSpec) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{18} -} - -func (x *SandboxSpec) GetLogLevel() string { - if x != nil { - return x.LogLevel - } - return "" -} - -func (x *SandboxSpec) GetEnvironment() map[string]string { - if x != nil { - return x.Environment - } - return nil -} - -func (x *SandboxSpec) GetTemplate() *SandboxTemplate { - if x != nil { - return x.Template - } - return nil -} - -func (x *SandboxSpec) GetPolicy() *sandboxv1.SandboxPolicy { - if x != nil { - return x.Policy - } - return nil -} - -func (x *SandboxSpec) GetProviders() []string { - if x != nil { - return x.Providers - } - return nil -} - -func (x *SandboxSpec) GetResourceRequirements() *ResourceRequirements { - if x != nil { - return x.ResourceRequirements - } - return nil -} - -func (x *SandboxSpec) GetCommand() []string { - if x != nil { - return x.Command - } - return nil -} - -func (x *SandboxSpec) GetTty() bool { - if x != nil { - return x.Tty - } - return false -} - -func (x *SandboxSpec) GetProviderAttachmentEpoch() string { - if x != nil { - return x.ProviderAttachmentEpoch - } - return "" -} - -func (x *SandboxSpec) GetRestartPolicy() SandboxRestartPolicy { - if x != nil { - return x.RestartPolicy - } - return SandboxRestartPolicy_SANDBOX_RESTART_POLICY_UNSPECIFIED -} - -type ResourceRequirements struct { - state protoimpl.MessageState `protogen:"open.v1"` - // GPU requirements for the sandbox. Presence indicates a GPU request. - Gpu *GpuResourceRequirements `protobuf:"bytes,1,opt,name=gpu,proto3" json:"gpu,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ResourceRequirements) Reset() { - *x = ResourceRequirements{} - mi := &file_openshell_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ResourceRequirements) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ResourceRequirements) ProtoMessage() {} - -func (x *ResourceRequirements) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[19] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ResourceRequirements.ProtoReflect.Descriptor instead. -func (*ResourceRequirements) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{19} -} - -func (x *ResourceRequirements) GetGpu() *GpuResourceRequirements { - if x != nil { - return x.Gpu - } - return nil -} - -// Public GPU resource requirements. -type GpuResourceRequirements struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Optional number of GPUs requested. When omitted, the request is for one - // GPU using the selected driver's default assignment behavior. - Count *uint32 `protobuf:"varint,1,opt,name=count,proto3,oneof" json:"count,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GpuResourceRequirements) Reset() { - *x = GpuResourceRequirements{} - mi := &file_openshell_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GpuResourceRequirements) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GpuResourceRequirements) ProtoMessage() {} - -func (x *GpuResourceRequirements) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[20] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GpuResourceRequirements.ProtoReflect.Descriptor instead. -func (*GpuResourceRequirements) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{20} -} - -func (x *GpuResourceRequirements) GetCount() uint32 { - if x != nil && x.Count != nil { - return *x.Count - } - return 0 -} - -// Historical inline compute template mapped onto compute-driver template inputs. -// -// Despite its name, this is not a reusable named sandbox template resource. It -// is an inline part of `SandboxSpec` kept for v1 compatibility. A future -// breaking API cleanup may rename this message to free `SandboxTemplate` for -// the reusable template resource now represented by `SandboxWorkloadTemplate`. -type SandboxTemplate struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Fully-qualified OCI image reference used to boot the sandbox. - Image string `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` - // Optional runtime class name requested from the compute platform. - RuntimeClassName string `protobuf:"bytes,2,opt,name=runtime_class_name,json=runtimeClassName,proto3" json:"runtime_class_name,omitempty"` - // Optional agent socket path exposed to the workload. - AgentSocket string `protobuf:"bytes,3,opt,name=agent_socket,json=agentSocket,proto3" json:"agent_socket,omitempty"` - // Labels applied to compute-platform resources for this sandbox. - Labels map[string]string `protobuf:"bytes,4,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Annotations applied to compute-platform resources for this sandbox. - Annotations map[string]string `protobuf:"bytes,5,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Additional environment variables injected by the template. - Environment map[string]string `protobuf:"bytes,6,rep,name=environment,proto3" json:"environment,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Platform-specific compute resource requirements and limits. - Resources *structpb.Struct `protobuf:"bytes,7,opt,name=resources,proto3" json:"resources,omitempty"` - // Enable Kubernetes user namespace isolation (hostUsers: false). - // When true, container UID 0 maps to a non-root host UID and capabilities - // become namespaced. Requires Kubernetes 1.33+ with user namespace support - // available (beta through 1.35, GA in 1.36+) and a supporting runtime. - // When unset, the cluster-wide default is used. - UserNamespaces *bool `protobuf:"varint,10,opt,name=user_namespaces,json=userNamespaces,proto3,oneof" json:"user_namespaces,omitempty"` - // Driver-keyed opaque config envelope supplied by the caller. - // The gateway selects the block matching the active compute driver and - // forwards only that inner Struct to DriverSandboxTemplate.driver_config. - // The selected driver owns nested schema validation. - DriverConfig *structpb.Struct `protobuf:"bytes,11,opt,name=driver_config,json=driverConfig,proto3" json:"driver_config,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxTemplate) Reset() { - *x = SandboxTemplate{} - mi := &file_openshell_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxTemplate) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxTemplate) ProtoMessage() {} - -func (x *SandboxTemplate) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[21] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxTemplate.ProtoReflect.Descriptor instead. -func (*SandboxTemplate) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{21} -} - -func (x *SandboxTemplate) GetImage() string { - if x != nil { - return x.Image - } - return "" -} - -func (x *SandboxTemplate) GetRuntimeClassName() string { - if x != nil { - return x.RuntimeClassName - } - return "" -} - -func (x *SandboxTemplate) GetAgentSocket() string { - if x != nil { - return x.AgentSocket - } - return "" -} - -func (x *SandboxTemplate) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - -func (x *SandboxTemplate) GetAnnotations() map[string]string { - if x != nil { - return x.Annotations - } - return nil -} - -func (x *SandboxTemplate) GetEnvironment() map[string]string { - if x != nil { - return x.Environment - } - return nil -} - -func (x *SandboxTemplate) GetResources() *structpb.Struct { - if x != nil { - return x.Resources - } - return nil -} - -func (x *SandboxTemplate) GetUserNamespaces() bool { - if x != nil && x.UserNamespaces != nil { - return *x.UserNamespaces - } - return false -} - -func (x *SandboxTemplate) GetDriverConfig() *structpb.Struct { - if x != nil { - return x.DriverConfig - } - return nil -} - -// Reusable named sandbox workload template resource. -// -// This is the actual workspace-scoped template resource used to create -// sandboxes by reference. It uses the longer name in v1 to avoid colliding with -// the historical inline `SandboxTemplate` message. A future breaking API -// cleanup may rename this resource to `SandboxTemplate`. -type SandboxWorkloadTemplate struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Kubernetes-style metadata (id, name, labels, timestamps, resource version). - Metadata *datamodelv1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - // Desired reusable workload shape and template-owned driver config. - Spec *SandboxWorkloadTemplateSpec `protobuf:"bytes,2,opt,name=spec,proto3" json:"spec,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxWorkloadTemplate) Reset() { - *x = SandboxWorkloadTemplate{} - mi := &file_openshell_proto_msgTypes[22] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxWorkloadTemplate) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxWorkloadTemplate) ProtoMessage() {} - -func (x *SandboxWorkloadTemplate) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[22] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxWorkloadTemplate.ProtoReflect.Descriptor instead. -func (*SandboxWorkloadTemplate) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{22} -} - -func (x *SandboxWorkloadTemplate) GetMetadata() *datamodelv1.ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *SandboxWorkloadTemplate) GetSpec() *SandboxWorkloadTemplateSpec { - if x != nil { - return x.Spec - } - return nil -} - -type SandboxWorkloadTemplateSpec struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Portable workload shape. - Workload *SandboxWorkloadConfig `protobuf:"bytes,1,opt,name=workload,proto3" json:"workload,omitempty"` - // Driver-keyed opaque config envelope supplied by the template owner. - DriverConfig *structpb.Struct `protobuf:"bytes,2,opt,name=driver_config,json=driverConfig,proto3" json:"driver_config,omitempty"` - // Desired service level associated with this template. - DesiredServiceLevel *SandboxServiceLevel `protobuf:"bytes,3,opt,name=desired_service_level,json=desiredServiceLevel,proto3" json:"desired_service_level,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxWorkloadTemplateSpec) Reset() { - *x = SandboxWorkloadTemplateSpec{} - mi := &file_openshell_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxWorkloadTemplateSpec) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxWorkloadTemplateSpec) ProtoMessage() {} - -func (x *SandboxWorkloadTemplateSpec) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[23] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxWorkloadTemplateSpec.ProtoReflect.Descriptor instead. -func (*SandboxWorkloadTemplateSpec) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{23} -} - -func (x *SandboxWorkloadTemplateSpec) GetWorkload() *SandboxWorkloadConfig { - if x != nil { - return x.Workload - } - return nil -} - -func (x *SandboxWorkloadTemplateSpec) GetDriverConfig() *structpb.Struct { - if x != nil { - return x.DriverConfig - } - return nil -} - -func (x *SandboxWorkloadTemplateSpec) GetDesiredServiceLevel() *SandboxServiceLevel { - if x != nil { - return x.DesiredServiceLevel - } - return nil -} - -type SandboxWorkloadConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Fully-qualified OCI image reference used to boot the sandbox. - Image string `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` - // Environment variables injected into the sandbox runtime. - Environment map[string]string `protobuf:"bytes,2,rep,name=environment,proto3" json:"environment,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Portable resource requirements for sandboxes created from this workload. - Resources *SandboxResources `protobuf:"bytes,3,opt,name=resources,proto3" json:"resources,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxWorkloadConfig) Reset() { - *x = SandboxWorkloadConfig{} - mi := &file_openshell_proto_msgTypes[24] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxWorkloadConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxWorkloadConfig) ProtoMessage() {} - -func (x *SandboxWorkloadConfig) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[24] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxWorkloadConfig.ProtoReflect.Descriptor instead. -func (*SandboxWorkloadConfig) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{24} -} - -func (x *SandboxWorkloadConfig) GetImage() string { - if x != nil { - return x.Image - } - return "" -} - -func (x *SandboxWorkloadConfig) GetEnvironment() map[string]string { - if x != nil { - return x.Environment - } - return nil -} - -func (x *SandboxWorkloadConfig) GetResources() *SandboxResources { - if x != nil { - return x.Resources - } - return nil -} - -type SandboxResources struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Portable CPU quantity, for example "500m" or "2". - Cpu string `protobuf:"bytes,1,opt,name=cpu,proto3" json:"cpu,omitempty"` - // Portable memory quantity, for example "512Mi" or "2Gi". - Memory string `protobuf:"bytes,2,opt,name=memory,proto3" json:"memory,omitempty"` - // GPU requirements for the sandbox workload. Presence indicates a GPU - // request. When count is omitted, the request uses the selected driver's - // default GPU assignment behavior. - Gpu *GpuResourceRequirements `protobuf:"bytes,3,opt,name=gpu,proto3" json:"gpu,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxResources) Reset() { - *x = SandboxResources{} - mi := &file_openshell_proto_msgTypes[25] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxResources) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxResources) ProtoMessage() {} - -func (x *SandboxResources) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[25] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxResources.ProtoReflect.Descriptor instead. -func (*SandboxResources) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{25} -} - -func (x *SandboxResources) GetCpu() string { - if x != nil { - return x.Cpu - } - return "" -} - -func (x *SandboxResources) GetMemory() string { - if x != nil { - return x.Memory - } - return "" -} - -func (x *SandboxResources) GetGpu() *GpuResourceRequirements { - if x != nil { - return x.Gpu - } - return nil -} - -type SandboxServiceLevel struct { - state protoimpl.MessageState `protogen:"open.v1"` - Startup *SandboxStartup `protobuf:"bytes,1,opt,name=startup,proto3" json:"startup,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxServiceLevel) Reset() { - *x = SandboxServiceLevel{} - mi := &file_openshell_proto_msgTypes[26] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxServiceLevel) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxServiceLevel) ProtoMessage() {} - -func (x *SandboxServiceLevel) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[26] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxServiceLevel.ProtoReflect.Descriptor instead. -func (*SandboxServiceLevel) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{26} -} - -func (x *SandboxServiceLevel) GetStartup() *SandboxStartup { - if x != nil { - return x.Startup - } - return nil -} - -type SandboxStartup struct { - state protoimpl.MessageState `protogen:"open.v1"` - ReadyWithin *durationpb.Duration `protobuf:"bytes,1,opt,name=ready_within,json=readyWithin,proto3" json:"ready_within,omitempty"` - MaxBurst uint32 `protobuf:"varint,2,opt,name=max_burst,json=maxBurst,proto3" json:"max_burst,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxStartup) Reset() { - *x = SandboxStartup{} - mi := &file_openshell_proto_msgTypes[27] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxStartup) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxStartup) ProtoMessage() {} - -func (x *SandboxStartup) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[27] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxStartup.ProtoReflect.Descriptor instead. -func (*SandboxStartup) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{27} -} - -func (x *SandboxStartup) GetReadyWithin() *durationpb.Duration { - if x != nil { - return x.ReadyWithin - } - return nil -} - -func (x *SandboxStartup) GetMaxBurst() uint32 { - if x != nil { - return x.MaxBurst - } - return 0 -} - -type SandboxWorkloadTemplateProvenance struct { - state protoimpl.MessageState `protogen:"open.v1"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - ResourceVersion string `protobuf:"bytes,2,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxWorkloadTemplateProvenance) Reset() { - *x = SandboxWorkloadTemplateProvenance{} - mi := &file_openshell_proto_msgTypes[28] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxWorkloadTemplateProvenance) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxWorkloadTemplateProvenance) ProtoMessage() {} - -func (x *SandboxWorkloadTemplateProvenance) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[28] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxWorkloadTemplateProvenance.ProtoReflect.Descriptor instead. -func (*SandboxWorkloadTemplateProvenance) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{28} -} - -func (x *SandboxWorkloadTemplateProvenance) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *SandboxWorkloadTemplateProvenance) GetResourceVersion() string { - if x != nil { - return x.ResourceVersion - } - return "" -} - -// User-facing sandbox status derived by the gateway from compute-driver observations. -// -// Public status does not embed driver-only flags such as `deleting`. -type SandboxStatus struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Name of the agent pod or equivalent runtime instance. - AgentPod string `protobuf:"bytes,2,opt,name=agent_pod,json=agentPod,proto3" json:"agent_pod,omitempty"` - // File descriptor or endpoint for reaching the agent service, when available. - AgentFd string `protobuf:"bytes,3,opt,name=agent_fd,json=agentFd,proto3" json:"agent_fd,omitempty"` - // File descriptor or endpoint for reaching the sandbox service, when available. - SandboxFd string `protobuf:"bytes,4,opt,name=sandbox_fd,json=sandboxFd,proto3" json:"sandbox_fd,omitempty"` - // Latest user-facing readiness and lifecycle conditions. - Conditions []*SandboxCondition `protobuf:"bytes,5,rep,name=conditions,proto3" json:"conditions,omitempty"` - // Gateway-derived lifecycle summary. - Phase SandboxPhase `protobuf:"varint,6,opt,name=phase,proto3,enum=openshell.v1.SandboxPhase" json:"phase,omitempty"` - // Currently active policy version (updated when sandbox reports loaded). - CurrentPolicyVersion uint32 `protobuf:"varint,7,opt,name=current_policy_version,json=currentPolicyVersion,proto3" json:"current_policy_version,omitempty"` - // Supervisor instance currently associated with the canonical main process. - // The gateway uses this to reject stale exit reports after a restart. - MainProcessInstanceId string `protobuf:"bytes,8,opt,name=main_process_instance_id,json=mainProcessInstanceId,proto3" json:"main_process_instance_id,omitempty"` - // Most recent normalized main process result. Signal exits use 128 + signal number. - // Cleared when a replacement main process becomes ready. - ExitCode *int32 `protobuf:"varint,9,opt,name=exit_code,json=exitCode,proto3,oneof" json:"exit_code,omitempty"` - // Last accepted network result for each configured tool server endpoint. - // Currently populated for MCP-over-HTTP endpoints. These passive results - // remain separate from sandbox lifecycle conditions and readiness. - EndpointStatuses []*EndpointStatus `protobuf:"bytes,10,rep,name=endpoint_statuses,json=endpointStatuses,proto3" json:"endpoint_statuses,omitempty"` - // Independent of infrastructure phase; retained across driver observations. - ConfigurationAdmission *SandboxConfigurationAdmission `protobuf:"bytes,11,opt,name=configuration_admission,json=configurationAdmission,proto3" json:"configuration_admission,omitempty"` - // Durable first-acceptance marker. Absent on legacy records; never reset by restart. - ConfigurationActivated *bool `protobuf:"varint,12,opt,name=configuration_activated,json=configurationActivated,proto3,oneof" json:"configuration_activated,omitempty"` - // Gateway-owned provisioning deadlines, retained after timeout for inspection and retry. - Provisioning *SandboxProvisioning `protobuf:"bytes,13,opt,name=provisioning,proto3" json:"provisioning,omitempty"` - // Consecutive policy-driven restart number in the current crash loop. - RestartCount uint32 `protobuf:"varint,14,opt,name=restart_count,json=restartCount,proto3" json:"restart_count,omitempty"` - // Deadline for the next restart attempt or recovery check. - NextRestartTime *timestamppb.Timestamp `protobuf:"bytes,15,opt,name=next_restart_time,json=nextRestartTime,proto3" json:"next_restart_time,omitempty"` - // Time when the current main process became ready. - MainProcessStartedTime *timestamppb.Timestamp `protobuf:"bytes,16,opt,name=main_process_started_time,json=mainProcessStartedTime,proto3" json:"main_process_started_time,omitempty"` - // Latest refused token grant across all outbound HTTP protocols, including - // failures before a configured endpoint or credential could be selected. - // Matching grant success or a policy, provider, or supervisor reset clears it. - LastTokenGrantFailure *TokenGrantFailureObservation `protobuf:"bytes,17,opt,name=last_token_grant_failure,json=lastTokenGrantFailure,proto3" json:"last_token_grant_failure,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxStatus) Reset() { - *x = SandboxStatus{} - mi := &file_openshell_proto_msgTypes[29] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxStatus) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxStatus) ProtoMessage() {} - -func (x *SandboxStatus) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[29] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxStatus.ProtoReflect.Descriptor instead. -func (*SandboxStatus) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{29} -} - -func (x *SandboxStatus) GetAgentPod() string { - if x != nil { - return x.AgentPod - } - return "" -} - -func (x *SandboxStatus) GetAgentFd() string { - if x != nil { - return x.AgentFd - } - return "" -} - -func (x *SandboxStatus) GetSandboxFd() string { - if x != nil { - return x.SandboxFd - } - return "" -} - -func (x *SandboxStatus) GetConditions() []*SandboxCondition { - if x != nil { - return x.Conditions - } - return nil -} - -func (x *SandboxStatus) GetPhase() SandboxPhase { - if x != nil { - return x.Phase - } - return SandboxPhase_SANDBOX_PHASE_UNSPECIFIED -} - -func (x *SandboxStatus) GetCurrentPolicyVersion() uint32 { - if x != nil { - return x.CurrentPolicyVersion - } - return 0 -} - -func (x *SandboxStatus) GetMainProcessInstanceId() string { - if x != nil { - return x.MainProcessInstanceId - } - return "" -} - -func (x *SandboxStatus) GetExitCode() int32 { - if x != nil && x.ExitCode != nil { - return *x.ExitCode - } - return 0 -} - -func (x *SandboxStatus) GetEndpointStatuses() []*EndpointStatus { - if x != nil { - return x.EndpointStatuses - } - return nil -} - -func (x *SandboxStatus) GetConfigurationAdmission() *SandboxConfigurationAdmission { - if x != nil { - return x.ConfigurationAdmission - } - return nil -} - -func (x *SandboxStatus) GetConfigurationActivated() bool { - if x != nil && x.ConfigurationActivated != nil { - return *x.ConfigurationActivated - } - return false -} - -func (x *SandboxStatus) GetProvisioning() *SandboxProvisioning { - if x != nil { - return x.Provisioning - } - return nil -} - -func (x *SandboxStatus) GetRestartCount() uint32 { - if x != nil { - return x.RestartCount - } - return 0 -} - -func (x *SandboxStatus) GetNextRestartTime() *timestamppb.Timestamp { - if x != nil { - return x.NextRestartTime - } - return nil -} - -func (x *SandboxStatus) GetMainProcessStartedTime() *timestamppb.Timestamp { - if x != nil { - return x.MainProcessStartedTime - } - return nil -} - -func (x *SandboxStatus) GetLastTokenGrantFailure() *TokenGrantFailureObservation { - if x != nil { - return x.LastTokenGrantFailure - } - return nil -} - -// User-facing sandbox condition derived from platform or gateway observations. -type SandboxCondition struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Condition class, typically mirroring the underlying platform condition type. - Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` - // Condition status value such as `True`, `False`, or `Unknown`. - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - // Short machine-readable reason associated with the condition. - Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"` - // Human-readable condition message. - Message string `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` - // Timestamp reported by the condition owner for the last transition. - TransitionTime *timestamppb.Timestamp `protobuf:"bytes,105,opt,name=transition_time,json=transitionTime,proto3" json:"transition_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxCondition) Reset() { - *x = SandboxCondition{} - mi := &file_openshell_proto_msgTypes[30] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxCondition) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxCondition) ProtoMessage() {} - -func (x *SandboxCondition) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[30] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxCondition.ProtoReflect.Descriptor instead. -func (*SandboxCondition) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{30} -} - -func (x *SandboxCondition) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -func (x *SandboxCondition) GetStatus() string { - if x != nil { - return x.Status - } - return "" -} - -func (x *SandboxCondition) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -func (x *SandboxCondition) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *SandboxCondition) GetTransitionTime() *timestamppb.Timestamp { - if x != nil { - return x.TransitionTime - } - return nil -} - -// Public platform event exposed on the sandbox watch stream. -type PlatformEvent struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Time when the event occurred. - EventTime *timestamppb.Timestamp `protobuf:"bytes,101,opt,name=event_time,json=eventTime,proto3" json:"event_time,omitempty"` - // Event source (e.g. "kubernetes", "docker", "process"). - Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` - // Event type/severity (e.g. "Normal", "Warning"). - Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` - // Short reason code (e.g. "Started", "Pulled", "Failed"). - Reason string `protobuf:"bytes,4,opt,name=reason,proto3" json:"reason,omitempty"` - // Human-readable event message. - Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` - // Optional metadata as key-value pairs. - Metadata map[string]string `protobuf:"bytes,6,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PlatformEvent) Reset() { - *x = PlatformEvent{} - mi := &file_openshell_proto_msgTypes[31] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PlatformEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PlatformEvent) ProtoMessage() {} - -func (x *PlatformEvent) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[31] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PlatformEvent.ProtoReflect.Descriptor instead. -func (*PlatformEvent) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{31} -} - -func (x *PlatformEvent) GetEventTime() *timestamppb.Timestamp { - if x != nil { - return x.EventTime - } - return nil -} - -func (x *PlatformEvent) GetSource() string { - if x != nil { - return x.Source - } - return "" -} - -func (x *PlatformEvent) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -func (x *PlatformEvent) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -func (x *PlatformEvent) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *PlatformEvent) GetMetadata() map[string]string { - if x != nil { - return x.Metadata - } - return nil -} - -// Create sandbox request. -type CreateSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,7,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Spec *SandboxSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` - // Optional user-supplied sandbox name. When empty the server generates one. - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // Optional labels for the sandbox (key-value metadata). - Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Optional annotations for the sandbox (non-selector metadata). Keys under - // the `internal.openshell.ai/` prefix are reserved for the gateway. - Annotations map[string]string `protobuf:"bytes,4,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // One-shot launch hint indicating that the creating client will attach to - // the canonical main process. The supervisor keeps the terminal transport - // alive until that attachment connects and closes naturally. - AwaitMainProcessAttachment bool `protobuf:"varint,5,opt,name=await_main_process_attachment,json=awaitMainProcessAttachment,proto3" json:"await_main_process_attachment,omitempty"` - // Workspace-scoped SandboxWorkloadTemplate name to resolve at creation time. - WorkloadTemplate string `protobuf:"bytes,6,opt,name=workload_template,json=workloadTemplate,proto3" json:"workload_template,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,8,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - // HTTP services to expose when the sandbox is created. Endpoints are - // registered after the sandbox has been persisted and route only while the - // sandbox is ready. - ServiceExposures []*SandboxServiceExposure `protobuf:"bytes,9,rep,name=service_exposures,json=serviceExposures,proto3" json:"service_exposures,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateSandboxRequest) Reset() { - *x = CreateSandboxRequest{} - mi := &file_openshell_proto_msgTypes[32] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateSandboxRequest) ProtoMessage() {} - -func (x *CreateSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[32] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateSandboxRequest.ProtoReflect.Descriptor instead. -func (*CreateSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{32} -} - -func (x *CreateSandboxRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *CreateSandboxRequest) GetSpec() *SandboxSpec { - if x != nil { - return x.Spec - } - return nil -} - -func (x *CreateSandboxRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *CreateSandboxRequest) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - -func (x *CreateSandboxRequest) GetAnnotations() map[string]string { - if x != nil { - return x.Annotations - } - return nil -} - -func (x *CreateSandboxRequest) GetAwaitMainProcessAttachment() bool { - if x != nil { - return x.AwaitMainProcessAttachment - } - return false -} - -func (x *CreateSandboxRequest) GetWorkloadTemplate() string { - if x != nil { - return x.WorkloadTemplate - } - return "" -} - -func (x *CreateSandboxRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -func (x *CreateSandboxRequest) GetServiceExposures() []*SandboxServiceExposure { - if x != nil { - return x.ServiceExposures - } - return nil -} - -// Update selected configuration of a stopped sandbox. Omitted fields are unchanged. -// Start explicitly after a successful update. With no fields selected, resume -// the pending operation stored by the gateway using the current resource version. -type UpdateSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,1,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Canonical name of the existing sandbox; this is not a rename or upsert. - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // Nonempty OCI workload image reference. This does not change trusted runtime - // images or reseed an existing workspace. - Image *string `protobuf:"bytes,3,opt,name=image,proto3,oneof" json:"image,omitempty"` - // Replace workload argv; an empty list restores image-resolved default shell behavior. - Command *SandboxCommandUpdate `protobuf:"bytes,6,opt,name=command,proto3" json:"command,omitempty"` - // Replace sandbox-level overrides. Empty clears overrides; template defaults remain. - Environment *SandboxMapUpdate `protobuf:"bytes,7,opt,name=environment,proto3" json:"environment,omitempty"` - // Replace user metadata, preserving gateway-owned keys. These do not set pod metadata. - Labels *SandboxMapUpdate `protobuf:"bytes,8,opt,name=labels,proto3" json:"labels,omitempty"` - Annotations *SandboxMapUpdate `protobuf:"bytes,9,opt,name=annotations,proto3" json:"annotations,omitempty"` - // Required nonzero resource version read by the caller. - ExpectedResourceVersion uint64 `protobuf:"varint,4,opt,name=expected_resource_version,json=expectedResourceVersion,proto3" json:"expected_resource_version,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. - RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UpdateSandboxRequest) Reset() { - *x = UpdateSandboxRequest{} - mi := &file_openshell_proto_msgTypes[33] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UpdateSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateSandboxRequest) ProtoMessage() {} - -func (x *UpdateSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[33] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateSandboxRequest.ProtoReflect.Descriptor instead. -func (*UpdateSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{33} -} - -func (x *UpdateSandboxRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *UpdateSandboxRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *UpdateSandboxRequest) GetImage() string { - if x != nil && x.Image != nil { - return *x.Image - } - return "" -} - -func (x *UpdateSandboxRequest) GetCommand() *SandboxCommandUpdate { - if x != nil { - return x.Command - } - return nil -} - -func (x *UpdateSandboxRequest) GetEnvironment() *SandboxMapUpdate { - if x != nil { - return x.Environment - } - return nil -} - -func (x *UpdateSandboxRequest) GetLabels() *SandboxMapUpdate { - if x != nil { - return x.Labels - } - return nil -} - -func (x *UpdateSandboxRequest) GetAnnotations() *SandboxMapUpdate { - if x != nil { - return x.Annotations - } - return nil -} - -func (x *UpdateSandboxRequest) GetExpectedResourceVersion() uint64 { - if x != nil { - return x.ExpectedResourceVersion - } - return 0 -} - -func (x *UpdateSandboxRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Presence selects replacement, including an explicitly empty list. -type SandboxCommandUpdate struct { - state protoimpl.MessageState `protogen:"open.v1"` - Argv []string `protobuf:"bytes,1,rep,name=argv,proto3" json:"argv,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxCommandUpdate) Reset() { - *x = SandboxCommandUpdate{} - mi := &file_openshell_proto_msgTypes[34] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxCommandUpdate) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxCommandUpdate) ProtoMessage() {} - -func (x *SandboxCommandUpdate) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[34] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxCommandUpdate.ProtoReflect.Descriptor instead. -func (*SandboxCommandUpdate) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{34} -} - -func (x *SandboxCommandUpdate) GetArgv() []string { - if x != nil { - return x.Argv - } - return nil -} - -// Presence selects replacement, including an explicitly empty map. -type SandboxMapUpdate struct { - state protoimpl.MessageState `protogen:"open.v1"` - Values map[string]string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxMapUpdate) Reset() { - *x = SandboxMapUpdate{} - mi := &file_openshell_proto_msgTypes[35] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxMapUpdate) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxMapUpdate) ProtoMessage() {} - -func (x *SandboxMapUpdate) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[35] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxMapUpdate.ProtoReflect.Descriptor instead. -func (*SandboxMapUpdate) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{35} -} - -func (x *SandboxMapUpdate) GetValues() map[string]string { - if x != nil { - return x.Values - } - return nil -} - -type CreateSandboxTemplateRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Template *SandboxWorkloadTemplate `protobuf:"bytes,1,opt,name=template,proto3" json:"template,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateSandboxTemplateRequest) Reset() { - *x = CreateSandboxTemplateRequest{} - mi := &file_openshell_proto_msgTypes[36] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateSandboxTemplateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateSandboxTemplateRequest) ProtoMessage() {} - -func (x *CreateSandboxTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[36] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateSandboxTemplateRequest.ProtoReflect.Descriptor instead. -func (*CreateSandboxTemplateRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{36} -} - -func (x *CreateSandboxTemplateRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *CreateSandboxTemplateRequest) GetTemplate() *SandboxWorkloadTemplate { - if x != nil { - return x.Template - } - return nil -} - -func (x *CreateSandboxTemplateRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -type GetSandboxTemplateRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxTemplateRequest) Reset() { - *x = GetSandboxTemplateRequest{} - mi := &file_openshell_proto_msgTypes[37] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxTemplateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxTemplateRequest) ProtoMessage() {} - -func (x *GetSandboxTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[37] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxTemplateRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxTemplateRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{37} -} - -func (x *GetSandboxTemplateRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetSandboxTemplateRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type ListSandboxTemplatesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Named and all-workspaces selections are accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,4,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // The maximum number of templates to return. Zero uses 100. Values above - // 1000 are coerced to 1000; negative values are invalid. - PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - // Token from a previous ListSandboxTemplates response. All other request - // parameters except page_size must match the request that produced it. - PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` - // Optional label selector in key=value comma-separated form. - LabelSelector string `protobuf:"bytes,3,opt,name=label_selector,json=labelSelector,proto3" json:"label_selector,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxTemplatesRequest) Reset() { - *x = ListSandboxTemplatesRequest{} - mi := &file_openshell_proto_msgTypes[38] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxTemplatesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxTemplatesRequest) ProtoMessage() {} - -func (x *ListSandboxTemplatesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[38] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxTemplatesRequest.ProtoReflect.Descriptor instead. -func (*ListSandboxTemplatesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{38} -} - -func (x *ListSandboxTemplatesRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ListSandboxTemplatesRequest) GetPageSize() int32 { - if x != nil { - return x.PageSize - } - return 0 -} - -func (x *ListSandboxTemplatesRequest) GetPageToken() string { - if x != nil { - return x.PageToken - } - return "" -} - -func (x *ListSandboxTemplatesRequest) GetLabelSelector() string { - if x != nil { - return x.LabelSelector - } - return "" -} - -type DeleteSandboxTemplateRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Succeed with ALREADY_ABSENT if the target is missing. Authorization and - // parent-workspace checks still apply. - AllowMissing bool `protobuf:"varint,3,opt,name=allow_missing,json=allowMissing,proto3" json:"allow_missing,omitempty"` - // Optional nonzero UUID. Same ID and payload replay success for 24 hours. - RequestId string `protobuf:"bytes,4,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteSandboxTemplateRequest) Reset() { - *x = DeleteSandboxTemplateRequest{} - mi := &file_openshell_proto_msgTypes[39] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteSandboxTemplateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteSandboxTemplateRequest) ProtoMessage() {} - -func (x *DeleteSandboxTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[39] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteSandboxTemplateRequest.ProtoReflect.Descriptor instead. -func (*DeleteSandboxTemplateRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{39} -} - -func (x *DeleteSandboxTemplateRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *DeleteSandboxTemplateRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *DeleteSandboxTemplateRequest) GetAllowMissing() bool { - if x != nil { - return x.AllowMissing - } - return false -} - -func (x *DeleteSandboxTemplateRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -type SandboxTemplateResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Template *SandboxWorkloadTemplate `protobuf:"bytes,1,opt,name=template,proto3" json:"template,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxTemplateResponse) Reset() { - *x = SandboxTemplateResponse{} - mi := &file_openshell_proto_msgTypes[40] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxTemplateResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxTemplateResponse) ProtoMessage() {} - -func (x *SandboxTemplateResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[40] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxTemplateResponse.ProtoReflect.Descriptor instead. -func (*SandboxTemplateResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{40} -} - -func (x *SandboxTemplateResponse) GetTemplate() *SandboxWorkloadTemplate { - if x != nil { - return x.Template - } - return nil -} - -type ListSandboxTemplatesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Templates []*SandboxWorkloadTemplate `protobuf:"bytes,1,rep,name=templates,proto3" json:"templates,omitempty"` - // Token for the next page. Empty when there are no subsequent pages. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxTemplatesResponse) Reset() { - *x = ListSandboxTemplatesResponse{} - mi := &file_openshell_proto_msgTypes[41] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxTemplatesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxTemplatesResponse) ProtoMessage() {} - -func (x *ListSandboxTemplatesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[41] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxTemplatesResponse.ProtoReflect.Descriptor instead. -func (*ListSandboxTemplatesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{41} -} - -func (x *ListSandboxTemplatesResponse) GetTemplates() []*SandboxWorkloadTemplate { - if x != nil { - return x.Templates - } - return nil -} - -func (x *ListSandboxTemplatesResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -type DeleteSandboxTemplateResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Outcome DeletionOutcome `protobuf:"varint,2,opt,name=outcome,proto3,enum=openshell.v1.DeletionOutcome" json:"outcome,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteSandboxTemplateResponse) Reset() { - *x = DeleteSandboxTemplateResponse{} - mi := &file_openshell_proto_msgTypes[42] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteSandboxTemplateResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteSandboxTemplateResponse) ProtoMessage() {} - -func (x *DeleteSandboxTemplateResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[42] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteSandboxTemplateResponse.ProtoReflect.Descriptor instead. -func (*DeleteSandboxTemplateResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{42} -} - -func (x *DeleteSandboxTemplateResponse) GetOutcome() DeletionOutcome { - if x != nil { - return x.Outcome - } - return DeletionOutcome_DELETION_OUTCOME_UNSPECIFIED -} - -// Request a gateway-owned staging slot for a local rootfs tar archive. -type BeginRootfsTarStagingRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Base file name of the local archive. The gateway uses it only to name the - // staged file; path separators and traversal components are rejected. - FileName string `protobuf:"bytes,1,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` - // Size of the local archive in bytes, checked against the driver limit - // before the gateway allocates a slot. - SizeBytes uint64 `protobuf:"varint,2,opt,name=size_bytes,json=sizeBytes,proto3" json:"size_bytes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *BeginRootfsTarStagingRequest) Reset() { - *x = BeginRootfsTarStagingRequest{} - mi := &file_openshell_proto_msgTypes[43] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *BeginRootfsTarStagingRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BeginRootfsTarStagingRequest) ProtoMessage() {} - -func (x *BeginRootfsTarStagingRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[43] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BeginRootfsTarStagingRequest.ProtoReflect.Descriptor instead. -func (*BeginRootfsTarStagingRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{43} -} - -func (x *BeginRootfsTarStagingRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *BeginRootfsTarStagingRequest) GetFileName() string { - if x != nil { - return x.FileName - } - return "" -} - -func (x *BeginRootfsTarStagingRequest) GetSizeBytes() uint64 { - if x != nil { - return x.SizeBytes - } - return 0 -} - -// Gateway-issued staging slot. -type BeginRootfsTarStagingResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Opaque single-use token. Pass it as - // `template.driver_config..rootfs_tar_staging_token` on - // CreateSandbox. The first CreateSandbox presenting it consumes it. - StagingToken string `protobuf:"bytes,1,opt,name=staging_token,json=stagingToken,proto3" json:"staging_token,omitempty"` - // Absolute path on the gateway host the client must write the archive to. - UploadPath string `protobuf:"bytes,2,opt,name=upload_path,json=uploadPath,proto3" json:"upload_path,omitempty"` - // Maximum accepted archive size in bytes, enforced again by the driver. - MaxBytes uint64 `protobuf:"varint,3,opt,name=max_bytes,json=maxBytes,proto3" json:"max_bytes,omitempty"` - // Wall-clock deadline after which the gateway reclaims the slot. - ExpirationTime *timestamppb.Timestamp `protobuf:"bytes,104,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *BeginRootfsTarStagingResponse) Reset() { - *x = BeginRootfsTarStagingResponse{} - mi := &file_openshell_proto_msgTypes[44] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *BeginRootfsTarStagingResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BeginRootfsTarStagingResponse) ProtoMessage() {} - -func (x *BeginRootfsTarStagingResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[44] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BeginRootfsTarStagingResponse.ProtoReflect.Descriptor instead. -func (*BeginRootfsTarStagingResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{44} -} - -func (x *BeginRootfsTarStagingResponse) GetStagingToken() string { - if x != nil { - return x.StagingToken - } - return "" -} - -func (x *BeginRootfsTarStagingResponse) GetUploadPath() string { - if x != nil { - return x.UploadPath - } - return "" -} - -func (x *BeginRootfsTarStagingResponse) GetMaxBytes() uint64 { - if x != nil { - return x.MaxBytes - } - return 0 -} - -func (x *BeginRootfsTarStagingResponse) GetExpirationTime() *timestamppb.Timestamp { - if x != nil { - return x.ExpirationTime - } - return nil -} - -// Get sandbox request. -type GetSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxRequest) Reset() { - *x = GetSandboxRequest{} - mi := &file_openshell_proto_msgTypes[45] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxRequest) ProtoMessage() {} - -func (x *GetSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[45] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{45} -} - -func (x *GetSandboxRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetSandboxRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// List sandboxes request. -type ListSandboxesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Named and all-workspaces selections are accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,4,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // The maximum number of sandboxes to return. Zero uses 100. Values above - // 1000 are coerced to 1000; negative values are invalid. - PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - // Token from a previous ListSandboxes response. All other request parameters - // except page_size must match the request that produced it. - PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` - // Optional label selector for filtering (format: "key1=value1,key2=value2"). - LabelSelector string `protobuf:"bytes,3,opt,name=label_selector,json=labelSelector,proto3" json:"label_selector,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxesRequest) Reset() { - *x = ListSandboxesRequest{} - mi := &file_openshell_proto_msgTypes[46] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxesRequest) ProtoMessage() {} - -func (x *ListSandboxesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[46] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxesRequest.ProtoReflect.Descriptor instead. -func (*ListSandboxesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{46} -} - -func (x *ListSandboxesRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ListSandboxesRequest) GetPageSize() int32 { - if x != nil { - return x.PageSize - } - return 0 -} - -func (x *ListSandboxesRequest) GetPageToken() string { - if x != nil { - return x.PageToken - } - return "" -} - -func (x *ListSandboxesRequest) GetLabelSelector() string { - if x != nil { - return x.LabelSelector - } - return "" -} - -// List providers attached to a sandbox request. -type ListSandboxProvidersRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // The maximum number of providers to return. Zero uses 100. Values above - // 1000 are coerced to 1000; negative values are invalid. - PageSize int32 `protobuf:"varint,3,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - // Token from a previous ListSandboxProviders response. All other request - // parameters except page_size must match the request that produced it. - PageToken string `protobuf:"bytes,4,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxProvidersRequest) Reset() { - *x = ListSandboxProvidersRequest{} - mi := &file_openshell_proto_msgTypes[47] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxProvidersRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxProvidersRequest) ProtoMessage() {} - -func (x *ListSandboxProvidersRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[47] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxProvidersRequest.ProtoReflect.Descriptor instead. -func (*ListSandboxProvidersRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{47} -} - -func (x *ListSandboxProvidersRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ListSandboxProvidersRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *ListSandboxProvidersRequest) GetPageSize() int32 { - if x != nil { - return x.PageSize - } - return 0 -} - -func (x *ListSandboxProvidersRequest) GetPageToken() string { - if x != nil { - return x.PageToken - } - return "" -} - -// Attach provider to sandbox request. -type AttachSandboxProviderRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,4,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Provider name to attach. - Provider string `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider,omitempty"` - // Expected resource version for optimistic concurrency control. - // If 0, the server uses the current version (backward compatibility). - // If non-zero, the server validates that the sandbox's current resource_version - // matches this value before applying the mutation, returning ABORTED on mismatch. - ExpectedResourceVersion uint64 `protobuf:"varint,3,opt,name=expected_resource_version,json=expectedResourceVersion,proto3" json:"expected_resource_version,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AttachSandboxProviderRequest) Reset() { - *x = AttachSandboxProviderRequest{} - mi := &file_openshell_proto_msgTypes[48] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AttachSandboxProviderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AttachSandboxProviderRequest) ProtoMessage() {} - -func (x *AttachSandboxProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[48] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AttachSandboxProviderRequest.ProtoReflect.Descriptor instead. -func (*AttachSandboxProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{48} -} - -func (x *AttachSandboxProviderRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *AttachSandboxProviderRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *AttachSandboxProviderRequest) GetProvider() string { - if x != nil { - return x.Provider - } - return "" -} - -func (x *AttachSandboxProviderRequest) GetExpectedResourceVersion() uint64 { - if x != nil { - return x.ExpectedResourceVersion - } - return 0 -} - -func (x *AttachSandboxProviderRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Detach provider from sandbox request. -type DetachSandboxProviderRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,4,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Provider name to detach. - Provider string `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider,omitempty"` - // Expected resource version for optimistic concurrency control. - // If 0, the server uses the current version (backward compatibility). - // If non-zero, the server validates that the sandbox's current resource_version - // matches this value before applying the mutation, returning ABORTED on mismatch. - ExpectedResourceVersion uint64 `protobuf:"varint,3,opt,name=expected_resource_version,json=expectedResourceVersion,proto3" json:"expected_resource_version,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DetachSandboxProviderRequest) Reset() { - *x = DetachSandboxProviderRequest{} - mi := &file_openshell_proto_msgTypes[49] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DetachSandboxProviderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DetachSandboxProviderRequest) ProtoMessage() {} - -func (x *DetachSandboxProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[49] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DetachSandboxProviderRequest.ProtoReflect.Descriptor instead. -func (*DetachSandboxProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{49} -} - -func (x *DetachSandboxProviderRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *DetachSandboxProviderRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *DetachSandboxProviderRequest) GetProvider() string { - if x != nil { - return x.Provider - } - return "" -} - -func (x *DetachSandboxProviderRequest) GetExpectedResourceVersion() uint64 { - if x != nil { - return x.ExpectedResourceVersion - } - return 0 -} - -func (x *DetachSandboxProviderRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Delete sandbox request. -type DeleteSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Canonical sandbox name. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Succeed with ALREADY_ABSENT if the target is missing. Does not wait for - // asynchronous cleanup and does not suppress authorization or parent errors. - AllowMissing bool `protobuf:"varint,3,opt,name=allow_missing,json=allowMissing,proto3" json:"allow_missing,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,4,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteSandboxRequest) Reset() { - *x = DeleteSandboxRequest{} - mi := &file_openshell_proto_msgTypes[50] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteSandboxRequest) ProtoMessage() {} - -func (x *DeleteSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[50] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteSandboxRequest.ProtoReflect.Descriptor instead. -func (*DeleteSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{50} -} - -func (x *DeleteSandboxRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *DeleteSandboxRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *DeleteSandboxRequest) GetAllowMissing() bool { - if x != nil { - return x.AllowMissing - } - return false -} - -func (x *DeleteSandboxRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Stop sandbox request. -type StopSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StopSandboxRequest) Reset() { - *x = StopSandboxRequest{} - mi := &file_openshell_proto_msgTypes[51] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StopSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StopSandboxRequest) ProtoMessage() {} - -func (x *StopSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[51] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StopSandboxRequest.ProtoReflect.Descriptor instead. -func (*StopSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{51} -} - -func (x *StopSandboxRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *StopSandboxRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *StopSandboxRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Start sandbox request. -type StartSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StartSandboxRequest) Reset() { - *x = StartSandboxRequest{} - mi := &file_openshell_proto_msgTypes[52] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StartSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StartSandboxRequest) ProtoMessage() {} - -func (x *StartSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[52] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StartSandboxRequest.ProtoReflect.Descriptor instead. -func (*StartSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{52} -} - -func (x *StartSandboxRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *StartSandboxRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *StartSandboxRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Suspend sandbox request. -type SuspendSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SuspendSandboxRequest) Reset() { - *x = SuspendSandboxRequest{} - mi := &file_openshell_proto_msgTypes[53] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SuspendSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SuspendSandboxRequest) ProtoMessage() {} - -func (x *SuspendSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[53] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SuspendSandboxRequest.ProtoReflect.Descriptor instead. -func (*SuspendSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{53} -} - -func (x *SuspendSandboxRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *SuspendSandboxRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// Resume sandbox request. -type ResumeSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // If true, the isolation backend may boot the workload cleanly when it - // cannot restore the retained memory state. If false, resume fails instead. - RestartIfRestoreFails bool `protobuf:"varint,3,opt,name=restart_if_restore_fails,json=restartIfRestoreFails,proto3" json:"restart_if_restore_fails,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ResumeSandboxRequest) Reset() { - *x = ResumeSandboxRequest{} - mi := &file_openshell_proto_msgTypes[54] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ResumeSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ResumeSandboxRequest) ProtoMessage() {} - -func (x *ResumeSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[54] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ResumeSandboxRequest.ProtoReflect.Descriptor instead. -func (*ResumeSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{54} -} - -func (x *ResumeSandboxRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ResumeSandboxRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ResumeSandboxRequest) GetRestartIfRestoreFails() bool { - if x != nil { - return x.RestartIfRestoreFails - } - return false -} - -// Sandbox response. -type SandboxResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Sandbox *Sandbox `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Service URLs created by CreateSandbox, keyed by service name. The empty - // key identifies the unnamed service. Other sandbox RPCs return an empty map. - ServiceUrls map[string]string `protobuf:"bytes,2,rep,name=service_urls,json=serviceUrls,proto3" json:"service_urls,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxResponse) Reset() { - *x = SandboxResponse{} - mi := &file_openshell_proto_msgTypes[55] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxResponse) ProtoMessage() {} - -func (x *SandboxResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[55] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxResponse.ProtoReflect.Descriptor instead. -func (*SandboxResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{55} -} - -func (x *SandboxResponse) GetSandbox() *Sandbox { - if x != nil { - return x.Sandbox - } - return nil -} - -func (x *SandboxResponse) GetServiceUrls() map[string]string { - if x != nil { - return x.ServiceUrls - } - return nil -} - -// List sandboxes response. -type ListSandboxesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Sandboxes []*Sandbox `protobuf:"bytes,1,rep,name=sandboxes,proto3" json:"sandboxes,omitempty"` - // Token for the next page. Empty when there are no subsequent pages. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxesResponse) Reset() { - *x = ListSandboxesResponse{} - mi := &file_openshell_proto_msgTypes[56] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxesResponse) ProtoMessage() {} - -func (x *ListSandboxesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[56] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxesResponse.ProtoReflect.Descriptor instead. -func (*ListSandboxesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{56} -} - -func (x *ListSandboxesResponse) GetSandboxes() []*Sandbox { - if x != nil { - return x.Sandboxes - } - return nil -} - -func (x *ListSandboxesResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -// List providers attached to a sandbox response. -type ListSandboxProvidersResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Providers []*datamodelv1.Provider `protobuf:"bytes,1,rep,name=providers,proto3" json:"providers,omitempty"` - // Token for the next page. Empty when there are no subsequent pages. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxProvidersResponse) Reset() { - *x = ListSandboxProvidersResponse{} - mi := &file_openshell_proto_msgTypes[57] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxProvidersResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxProvidersResponse) ProtoMessage() {} - -func (x *ListSandboxProvidersResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[57] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxProvidersResponse.ProtoReflect.Descriptor instead. -func (*ListSandboxProvidersResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{57} -} - -func (x *ListSandboxProvidersResponse) GetProviders() []*datamodelv1.Provider { - if x != nil { - return x.Providers - } - return nil -} - -func (x *ListSandboxProvidersResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -// Attach provider to sandbox response. -type AttachSandboxProviderResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Sandbox *Sandbox `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // True when the provider was newly attached. False means it was already attached. - Attached bool `protobuf:"varint,2,opt,name=attached,proto3" json:"attached,omitempty"` - // Persisted intent; readiness requires current supervisor observations. - Receipt *ProviderMutationReceipt `protobuf:"bytes,3,opt,name=receipt,proto3" json:"receipt,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AttachSandboxProviderResponse) Reset() { - *x = AttachSandboxProviderResponse{} - mi := &file_openshell_proto_msgTypes[58] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AttachSandboxProviderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AttachSandboxProviderResponse) ProtoMessage() {} - -func (x *AttachSandboxProviderResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[58] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AttachSandboxProviderResponse.ProtoReflect.Descriptor instead. -func (*AttachSandboxProviderResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{58} -} - -func (x *AttachSandboxProviderResponse) GetSandbox() *Sandbox { - if x != nil { - return x.Sandbox - } - return nil -} - -func (x *AttachSandboxProviderResponse) GetAttached() bool { - if x != nil { - return x.Attached - } - return false -} - -func (x *AttachSandboxProviderResponse) GetReceipt() *ProviderMutationReceipt { - if x != nil { - return x.Receipt - } - return nil -} - -// Detach provider from sandbox response. -type DetachSandboxProviderResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Sandbox *Sandbox `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // True when the provider was removed. False means it was not attached. - Detached bool `protobuf:"varint,2,opt,name=detached,proto3" json:"detached,omitempty"` - // Revocation is complete only when this receipt reports REVOKED. - Receipt *ProviderMutationReceipt `protobuf:"bytes,3,opt,name=receipt,proto3" json:"receipt,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DetachSandboxProviderResponse) Reset() { - *x = DetachSandboxProviderResponse{} - mi := &file_openshell_proto_msgTypes[59] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DetachSandboxProviderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DetachSandboxProviderResponse) ProtoMessage() {} - -func (x *DetachSandboxProviderResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[59] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DetachSandboxProviderResponse.ProtoReflect.Descriptor instead. -func (*DetachSandboxProviderResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{59} -} - -func (x *DetachSandboxProviderResponse) GetSandbox() *Sandbox { - if x != nil { - return x.Sandbox - } - return nil -} - -func (x *DetachSandboxProviderResponse) GetDetached() bool { - if x != nil { - return x.Detached - } - return false -} - -func (x *DetachSandboxProviderResponse) GetReceipt() *ProviderMutationReceipt { - if x != nil { - return x.Receipt - } - return nil -} - -// Exact desired authority. Revisions are opaque identities, never ordered. -type ProviderDesiredIdentity struct { - state protoimpl.MessageState `protogen:"open.v1"` - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - Sandbox string `protobuf:"bytes,2,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - AttachmentEpoch string `protobuf:"bytes,3,opt,name=attachment_epoch,json=attachmentEpoch,proto3" json:"attachment_epoch,omitempty"` - // Empty for a detached provider. - ProviderId string `protobuf:"bytes,4,opt,name=provider_id,json=providerId,proto3" json:"provider_id,omitempty"` - ProviderResourceVersion uint64 `protobuf:"varint,5,opt,name=provider_resource_version,json=providerResourceVersion,proto3" json:"provider_resource_version,omitempty"` - ProviderEnvRevision uint64 `protobuf:"varint,6,opt,name=provider_env_revision,json=providerEnvRevision,proto3" json:"provider_env_revision,omitempty"` - ConfigRevision uint64 `protobuf:"varint,7,opt,name=config_revision,json=configRevision,proto3" json:"config_revision,omitempty"` - PolicyHash string `protobuf:"bytes,8,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderDesiredIdentity) Reset() { - *x = ProviderDesiredIdentity{} - mi := &file_openshell_proto_msgTypes[60] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderDesiredIdentity) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderDesiredIdentity) ProtoMessage() {} - -func (x *ProviderDesiredIdentity) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[60] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderDesiredIdentity.ProtoReflect.Descriptor instead. -func (*ProviderDesiredIdentity) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{60} -} - -func (x *ProviderDesiredIdentity) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *ProviderDesiredIdentity) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *ProviderDesiredIdentity) GetAttachmentEpoch() string { - if x != nil { - return x.AttachmentEpoch - } - return "" -} - -func (x *ProviderDesiredIdentity) GetProviderId() string { - if x != nil { - return x.ProviderId - } - return "" -} - -func (x *ProviderDesiredIdentity) GetProviderResourceVersion() uint64 { - if x != nil { - return x.ProviderResourceVersion - } - return 0 -} - -func (x *ProviderDesiredIdentity) GetProviderEnvRevision() uint64 { - if x != nil { - return x.ProviderEnvRevision - } - return 0 -} - -func (x *ProviderDesiredIdentity) GetConfigRevision() uint64 { - if x != nil { - return x.ConfigRevision - } - return 0 -} - -func (x *ProviderDesiredIdentity) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -// Identifies one component snapshot revision. Revisions are equality tokens, -// not members of one shared ordering domain. -type ConfigSnapshotRevision struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Component: - // - // *ConfigSnapshotRevision_SandboxConfig - // *ConfigSnapshotRevision_ProviderEnvironment - // *ConfigSnapshotRevision_ProviderTarget - Component isConfigSnapshotRevision_Component `protobuf_oneof:"component"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ConfigSnapshotRevision) Reset() { - *x = ConfigSnapshotRevision{} - mi := &file_openshell_proto_msgTypes[61] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ConfigSnapshotRevision) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConfigSnapshotRevision) ProtoMessage() {} - -func (x *ConfigSnapshotRevision) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[61] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConfigSnapshotRevision.ProtoReflect.Descriptor instead. -func (*ConfigSnapshotRevision) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{61} -} - -func (x *ConfigSnapshotRevision) GetComponent() isConfigSnapshotRevision_Component { - if x != nil { - return x.Component - } - return nil -} - -func (x *ConfigSnapshotRevision) GetSandboxConfig() *SandboxConfigRevision { - if x != nil { - if x, ok := x.Component.(*ConfigSnapshotRevision_SandboxConfig); ok { - return x.SandboxConfig - } - } - return nil -} - -func (x *ConfigSnapshotRevision) GetProviderEnvironment() uint64 { - if x != nil { - if x, ok := x.Component.(*ConfigSnapshotRevision_ProviderEnvironment); ok { - return x.ProviderEnvironment - } - } - return 0 -} - -func (x *ConfigSnapshotRevision) GetProviderTarget() *ProviderDesiredIdentity { - if x != nil { - if x, ok := x.Component.(*ConfigSnapshotRevision_ProviderTarget); ok { - return x.ProviderTarget - } - } - return nil -} - -type isConfigSnapshotRevision_Component interface { - isConfigSnapshotRevision_Component() -} - -type ConfigSnapshotRevision_SandboxConfig struct { - SandboxConfig *SandboxConfigRevision `protobuf:"bytes,1,opt,name=sandbox_config,json=sandboxConfig,proto3,oneof"` -} - -type ConfigSnapshotRevision_ProviderEnvironment struct { - ProviderEnvironment uint64 `protobuf:"varint,2,opt,name=provider_environment,json=providerEnvironment,proto3,oneof"` -} - -type ConfigSnapshotRevision_ProviderTarget struct { - // Complete desired authority for one sandbox-scoped provider mutation. - ProviderTarget *ProviderDesiredIdentity `protobuf:"bytes,3,opt,name=provider_target,json=providerTarget,proto3,oneof"` -} - -func (*ConfigSnapshotRevision_SandboxConfig) isConfigSnapshotRevision_Component() {} - -func (*ConfigSnapshotRevision_ProviderEnvironment) isConfigSnapshotRevision_Component() {} - -func (*ConfigSnapshotRevision_ProviderTarget) isConfigSnapshotRevision_Component() {} - -// Identity needed to correlate effective sandbox configuration with the -// policy-history row whose apply status the gateway records. -type SandboxConfigRevision struct { - state protoimpl.MessageState `protogen:"open.v1"` - ConfigRevision uint64 `protobuf:"varint,1,opt,name=config_revision,json=configRevision,proto3" json:"config_revision,omitempty"` - PolicyVersion uint32 `protobuf:"varint,2,opt,name=policy_version,json=policyVersion,proto3" json:"policy_version,omitempty"` - PolicySource sandboxv1.PolicySource `protobuf:"varint,3,opt,name=policy_source,json=policySource,proto3,enum=openshell.sandbox.v1.PolicySource" json:"policy_source,omitempty"` - GlobalPolicyVersion uint32 `protobuf:"varint,4,opt,name=global_policy_version,json=globalPolicyVersion,proto3" json:"global_policy_version,omitempty"` - // Monotonic revision of the sandbox-scoped settings row. This disambiguates - // setting operations whose effective config fingerprint is equality-only. - SettingsRevision uint64 `protobuf:"varint,5,opt,name=settings_revision,json=settingsRevision,proto3" json:"settings_revision,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxConfigRevision) Reset() { - *x = SandboxConfigRevision{} - mi := &file_openshell_proto_msgTypes[62] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxConfigRevision) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxConfigRevision) ProtoMessage() {} - -func (x *SandboxConfigRevision) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[62] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxConfigRevision.ProtoReflect.Descriptor instead. -func (*SandboxConfigRevision) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{62} -} - -func (x *SandboxConfigRevision) GetConfigRevision() uint64 { - if x != nil { - return x.ConfigRevision - } - return 0 -} - -func (x *SandboxConfigRevision) GetPolicyVersion() uint32 { - if x != nil { - return x.PolicyVersion - } - return 0 -} - -func (x *SandboxConfigRevision) GetPolicySource() sandboxv1.PolicySource { - if x != nil { - return x.PolicySource - } - return sandboxv1.PolicySource(0) -} - -func (x *SandboxConfigRevision) GetGlobalPolicyVersion() uint32 { - if x != nil { - return x.GlobalPolicyVersion - } - return 0 -} - -func (x *SandboxConfigRevision) GetSettingsRevision() uint64 { - if x != nil { - return x.SettingsRevision - } - return 0 -} - -// Durable progress for one sandbox-scoped desired-state mutation. Snapshot -// contents and credentials are never stored in this resource. -type ConfigUpdateOperation struct { - state protoimpl.MessageState `protogen:"open.v1"` - OperationId string `protobuf:"bytes,1,opt,name=operation_id,json=operationId,proto3" json:"operation_id,omitempty"` - SandboxId string `protobuf:"bytes,2,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - Component ConfigComponent `protobuf:"varint,3,opt,name=component,proto3,enum=openshell.v1.ConfigComponent" json:"component,omitempty"` - TargetRevision *ConfigSnapshotRevision `protobuf:"bytes,4,opt,name=target_revision,json=targetRevision,proto3" json:"target_revision,omitempty"` - State ConfigUpdateOperationState `protobuf:"varint,5,opt,name=state,proto3,enum=openshell.v1.ConfigUpdateOperationState" json:"state,omitempty"` - Outcome ConfigApplyOutcome `protobuf:"varint,6,opt,name=outcome,proto3,enum=openshell.v1.ConfigApplyOutcome" json:"outcome,omitempty"` - SanitizedError string `protobuf:"bytes,7,opt,name=sanitized_error,json=sanitizedError,proto3" json:"sanitized_error,omitempty"` - CreatedTime *timestamppb.Timestamp `protobuf:"bytes,108,opt,name=created_time,json=createdTime,proto3" json:"created_time,omitempty"` - UpdatedTime *timestamppb.Timestamp `protobuf:"bytes,109,opt,name=updated_time,json=updatedTime,proto3" json:"updated_time,omitempty"` - // Absent until the operation reaches a terminal state. - CompletedTime *timestamppb.Timestamp `protobuf:"bytes,110,opt,name=completed_time,json=completedTime,proto3" json:"completed_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ConfigUpdateOperation) Reset() { - *x = ConfigUpdateOperation{} - mi := &file_openshell_proto_msgTypes[63] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ConfigUpdateOperation) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConfigUpdateOperation) ProtoMessage() {} - -func (x *ConfigUpdateOperation) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[63] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConfigUpdateOperation.ProtoReflect.Descriptor instead. -func (*ConfigUpdateOperation) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{63} -} - -func (x *ConfigUpdateOperation) GetOperationId() string { - if x != nil { - return x.OperationId - } - return "" -} - -func (x *ConfigUpdateOperation) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *ConfigUpdateOperation) GetComponent() ConfigComponent { - if x != nil { - return x.Component - } - return ConfigComponent_CONFIG_COMPONENT_UNSPECIFIED -} - -func (x *ConfigUpdateOperation) GetTargetRevision() *ConfigSnapshotRevision { - if x != nil { - return x.TargetRevision - } - return nil -} - -func (x *ConfigUpdateOperation) GetState() ConfigUpdateOperationState { - if x != nil { - return x.State - } - return ConfigUpdateOperationState_CONFIG_UPDATE_OPERATION_STATE_UNSPECIFIED -} - -func (x *ConfigUpdateOperation) GetOutcome() ConfigApplyOutcome { - if x != nil { - return x.Outcome - } - return ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_UNSPECIFIED -} - -func (x *ConfigUpdateOperation) GetSanitizedError() string { - if x != nil { - return x.SanitizedError - } - return "" -} - -func (x *ConfigUpdateOperation) GetCreatedTime() *timestamppb.Timestamp { - if x != nil { - return x.CreatedTime - } - return nil -} - -func (x *ConfigUpdateOperation) GetUpdatedTime() *timestamppb.Timestamp { - if x != nil { - return x.UpdatedTime - } - return nil -} - -func (x *ConfigUpdateOperation) GetCompletedTime() *timestamppb.Timestamp { - if x != nil { - return x.CompletedTime - } - return nil -} - -// Immutable, secret-free record of one sandbox's intended provider mutation. -type ProviderMutationReceipt struct { - state protoimpl.MessageState `protogen:"open.v1"` - ReceiptId string `protobuf:"bytes,1,opt,name=receipt_id,json=receiptId,proto3" json:"receipt_id,omitempty"` - // Shared by all sandbox receipts from one provider update. - MutationId string `protobuf:"bytes,2,opt,name=mutation_id,json=mutationId,proto3" json:"mutation_id,omitempty"` - Provider string `protobuf:"bytes,3,opt,name=provider,proto3" json:"provider,omitempty"` - Workspace string `protobuf:"bytes,4,opt,name=workspace,proto3" json:"workspace,omitempty"` - Kind ProviderMutationKind `protobuf:"varint,5,opt,name=kind,proto3,enum=openshell.v1.ProviderMutationKind" json:"kind,omitempty"` - Desired *ProviderDesiredIdentity `protobuf:"bytes,6,opt,name=desired,proto3" json:"desired,omitempty"` - PersistedTime *timestamppb.Timestamp `protobuf:"bytes,107,opt,name=persisted_time,json=persistedTime,proto3" json:"persisted_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderMutationReceipt) Reset() { - *x = ProviderMutationReceipt{} - mi := &file_openshell_proto_msgTypes[64] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderMutationReceipt) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderMutationReceipt) ProtoMessage() {} - -func (x *ProviderMutationReceipt) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[64] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderMutationReceipt.ProtoReflect.Descriptor instead. -func (*ProviderMutationReceipt) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{64} -} - -func (x *ProviderMutationReceipt) GetReceiptId() string { - if x != nil { - return x.ReceiptId - } - return "" -} - -func (x *ProviderMutationReceipt) GetMutationId() string { - if x != nil { - return x.MutationId - } - return "" -} - -func (x *ProviderMutationReceipt) GetProvider() string { - if x != nil { - return x.Provider - } - return "" -} - -func (x *ProviderMutationReceipt) GetWorkspace() string { - if x != nil { - return x.Workspace - } - return "" -} - -func (x *ProviderMutationReceipt) GetKind() ProviderMutationKind { - if x != nil { - return x.Kind - } - return ProviderMutationKind_PROVIDER_MUTATION_KIND_UNSPECIFIED -} - -func (x *ProviderMutationReceipt) GetDesired() *ProviderDesiredIdentity { - if x != nil { - return x.Desired - } - return nil -} - -func (x *ProviderMutationReceipt) GetPersistedTime() *timestamppb.Timestamp { - if x != nil { - return x.PersistedTime - } - return nil -} - -// Installed state reported by the current supervisor. Process installation is -// acknowledged by the authenticated sandbox boundary after replacing its -// environment for future process launches. -type ProviderReadinessObservation struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Gateway-issued identifier from the current ConnectSupervisor response. - SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` - // Monotonic only within this connection; unrelated to revision fingerprints. - Sequence uint64 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"` - AttachmentEpoch string `protobuf:"bytes,3,opt,name=attachment_epoch,json=attachmentEpoch,proto3" json:"attachment_epoch,omitempty"` - ProviderEnvRevision uint64 `protobuf:"varint,4,opt,name=provider_env_revision,json=providerEnvRevision,proto3" json:"provider_env_revision,omitempty"` - ConfigRevision uint64 `protobuf:"varint,5,opt,name=config_revision,json=configRevision,proto3" json:"config_revision,omitempty"` - PolicyHash string `protobuf:"bytes,6,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - CredentialsInstalled bool `protobuf:"varint,7,opt,name=credentials_installed,json=credentialsInstalled,proto3" json:"credentials_installed,omitempty"` - PolicyActive bool `protobuf:"varint,8,opt,name=policy_active,json=policyActive,proto3" json:"policy_active,omitempty"` - LaunchEnvironmentInstalled bool `protobuf:"varint,9,opt,name=launch_environment_installed,json=launchEnvironmentInstalled,proto3" json:"launch_environment_installed,omitempty"` - ProcessInstanceId string `protobuf:"bytes,10,opt,name=process_instance_id,json=processInstanceId,proto3" json:"process_instance_id,omitempty"` - Reason ProviderReadinessReason `protobuf:"varint,11,opt,name=reason,proto3,enum=openshell.v1.ProviderReadinessReason" json:"reason,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderReadinessObservation) Reset() { - *x = ProviderReadinessObservation{} - mi := &file_openshell_proto_msgTypes[65] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderReadinessObservation) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderReadinessObservation) ProtoMessage() {} - -func (x *ProviderReadinessObservation) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[65] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderReadinessObservation.ProtoReflect.Descriptor instead. -func (*ProviderReadinessObservation) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{65} -} - -func (x *ProviderReadinessObservation) GetSessionId() string { - if x != nil { - return x.SessionId - } - return "" -} - -func (x *ProviderReadinessObservation) GetSequence() uint64 { - if x != nil { - return x.Sequence - } - return 0 -} - -func (x *ProviderReadinessObservation) GetAttachmentEpoch() string { - if x != nil { - return x.AttachmentEpoch - } - return "" -} - -func (x *ProviderReadinessObservation) GetProviderEnvRevision() uint64 { - if x != nil { - return x.ProviderEnvRevision - } - return 0 -} - -func (x *ProviderReadinessObservation) GetConfigRevision() uint64 { - if x != nil { - return x.ConfigRevision - } - return 0 -} - -func (x *ProviderReadinessObservation) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *ProviderReadinessObservation) GetCredentialsInstalled() bool { - if x != nil { - return x.CredentialsInstalled - } - return false -} - -func (x *ProviderReadinessObservation) GetPolicyActive() bool { - if x != nil { - return x.PolicyActive - } - return false -} - -func (x *ProviderReadinessObservation) GetLaunchEnvironmentInstalled() bool { - if x != nil { - return x.LaunchEnvironmentInstalled - } - return false -} - -func (x *ProviderReadinessObservation) GetProcessInstanceId() string { - if x != nil { - return x.ProcessInstanceId - } - return "" -} - -func (x *ProviderReadinessObservation) GetReason() ProviderReadinessReason { - if x != nil { - return x.Reason - } - return ProviderReadinessReason_PROVIDER_READINESS_REASON_UNSPECIFIED -} - -// Operator view of desired and observed state; contains no credential material. -type ProviderReadinessStatus struct { - state protoimpl.MessageState `protogen:"open.v1"` - Receipt *ProviderMutationReceipt `protobuf:"bytes,1,opt,name=receipt,proto3" json:"receipt,omitempty"` - State ProviderReadinessState `protobuf:"varint,2,opt,name=state,proto3,enum=openshell.v1.ProviderReadinessState" json:"state,omitempty"` - Reason ProviderReadinessReason `protobuf:"varint,3,opt,name=reason,proto3,enum=openshell.v1.ProviderReadinessReason" json:"reason,omitempty"` - Observed *ProviderReadinessObservation `protobuf:"bytes,4,opt,name=observed,proto3" json:"observed,omitempty"` - NetworkInstanceId string `protobuf:"bytes,5,opt,name=network_instance_id,json=networkInstanceId,proto3" json:"network_instance_id,omitempty"` - // Absent until the current supervisor session supplies accepted evidence. - ObservedTime *timestamppb.Timestamp `protobuf:"bytes,106,opt,name=observed_time,json=observedTime,proto3" json:"observed_time,omitempty"` - EvaluatedTime *timestamppb.Timestamp `protobuf:"bytes,107,opt,name=evaluated_time,json=evaluatedTime,proto3" json:"evaluated_time,omitempty"` - // Durable operation for the receipt, including its terminal apply outcome. - Operation *ConfigUpdateOperation `protobuf:"bytes,8,opt,name=operation,proto3" json:"operation,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderReadinessStatus) Reset() { - *x = ProviderReadinessStatus{} - mi := &file_openshell_proto_msgTypes[66] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderReadinessStatus) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderReadinessStatus) ProtoMessage() {} - -func (x *ProviderReadinessStatus) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[66] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderReadinessStatus.ProtoReflect.Descriptor instead. -func (*ProviderReadinessStatus) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{66} -} - -func (x *ProviderReadinessStatus) GetReceipt() *ProviderMutationReceipt { - if x != nil { - return x.Receipt - } - return nil -} - -func (x *ProviderReadinessStatus) GetState() ProviderReadinessState { - if x != nil { - return x.State - } - return ProviderReadinessState_PROVIDER_READINESS_STATE_UNSPECIFIED -} - -func (x *ProviderReadinessStatus) GetReason() ProviderReadinessReason { - if x != nil { - return x.Reason - } - return ProviderReadinessReason_PROVIDER_READINESS_REASON_UNSPECIFIED -} - -func (x *ProviderReadinessStatus) GetObserved() *ProviderReadinessObservation { - if x != nil { - return x.Observed - } - return nil -} - -func (x *ProviderReadinessStatus) GetNetworkInstanceId() string { - if x != nil { - return x.NetworkInstanceId - } - return "" -} - -func (x *ProviderReadinessStatus) GetObservedTime() *timestamppb.Timestamp { - if x != nil { - return x.ObservedTime - } - return nil -} - -func (x *ProviderReadinessStatus) GetEvaluatedTime() *timestamppb.Timestamp { - if x != nil { - return x.EvaluatedTime - } - return nil -} - -func (x *ProviderReadinessStatus) GetOperation() *ConfigUpdateOperation { - if x != nil { - return x.Operation - } - return nil -} - -// Query an immutable receipt, or reconstruct the current desired state when -// receipt_id is empty. The sandbox identity must match the receipt. -type GetSandboxProviderStatusRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,4,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - Provider string `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider,omitempty"` - ReceiptId string `protobuf:"bytes,3,opt,name=receipt_id,json=receiptId,proto3" json:"receipt_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxProviderStatusRequest) Reset() { - *x = GetSandboxProviderStatusRequest{} - mi := &file_openshell_proto_msgTypes[67] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxProviderStatusRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxProviderStatusRequest) ProtoMessage() {} - -func (x *GetSandboxProviderStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[67] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxProviderStatusRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxProviderStatusRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{67} -} - -func (x *GetSandboxProviderStatusRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetSandboxProviderStatusRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *GetSandboxProviderStatusRequest) GetProvider() string { - if x != nil { - return x.Provider - } - return "" -} - -func (x *GetSandboxProviderStatusRequest) GetReceiptId() string { - if x != nil { - return x.ReceiptId - } - return "" -} - -type GetSandboxProviderStatusResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Status *ProviderReadinessStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxProviderStatusResponse) Reset() { - *x = GetSandboxProviderStatusResponse{} - mi := &file_openshell_proto_msgTypes[68] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxProviderStatusResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxProviderStatusResponse) ProtoMessage() {} - -func (x *GetSandboxProviderStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[68] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxProviderStatusResponse.ProtoReflect.Descriptor instead. -func (*GetSandboxProviderStatusResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{68} -} - -func (x *GetSandboxProviderStatusResponse) GetStatus() *ProviderReadinessStatus { - if x != nil { - return x.Status - } - return nil -} - -// Installation evidence from the authenticated supervisor for this sandbox. -// A caller-provided instance identifier alone never establishes authority. -type ReportProviderReadinessRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - Observation *ProviderReadinessObservation `protobuf:"bytes,2,opt,name=observation,proto3" json:"observation,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReportProviderReadinessRequest) Reset() { - *x = ReportProviderReadinessRequest{} - mi := &file_openshell_proto_msgTypes[69] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReportProviderReadinessRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportProviderReadinessRequest) ProtoMessage() {} - -func (x *ReportProviderReadinessRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[69] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportProviderReadinessRequest.ProtoReflect.Descriptor instead. -func (*ReportProviderReadinessRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{69} -} - -func (x *ReportProviderReadinessRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *ReportProviderReadinessRequest) GetObservation() *ProviderReadinessObservation { - if x != nil { - return x.Observation - } - return nil -} - -// Acknowledges accepted evidence without granting a separate session authority. -// Identical retries do not extend the evidence's original acceptance time. -type ReportProviderReadinessResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - AcceptedSequence uint64 `protobuf:"varint,1,opt,name=accepted_sequence,json=acceptedSequence,proto3" json:"accepted_sequence,omitempty"` - ReportInterval *durationpb.Duration `protobuf:"bytes,102,opt,name=report_interval,json=reportInterval,proto3" json:"report_interval,omitempty"` - ObservationTtl *durationpb.Duration `protobuf:"bytes,103,opt,name=observation_ttl,json=observationTtl,proto3" json:"observation_ttl,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReportProviderReadinessResponse) Reset() { - *x = ReportProviderReadinessResponse{} - mi := &file_openshell_proto_msgTypes[70] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReportProviderReadinessResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportProviderReadinessResponse) ProtoMessage() {} - -func (x *ReportProviderReadinessResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[70] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportProviderReadinessResponse.ProtoReflect.Descriptor instead. -func (*ReportProviderReadinessResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{70} -} - -func (x *ReportProviderReadinessResponse) GetAcceptedSequence() uint64 { - if x != nil { - return x.AcceptedSequence - } - return 0 -} - -func (x *ReportProviderReadinessResponse) GetReportInterval() *durationpb.Duration { - if x != nil { - return x.ReportInterval - } - return nil -} - -func (x *ReportProviderReadinessResponse) GetObservationTtl() *durationpb.Duration { - if x != nil { - return x.ObservationTtl - } - return nil -} - -// Delete sandbox response. -type DeleteSandboxResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Outcome DeletionOutcome `protobuf:"varint,2,opt,name=outcome,proto3,enum=openshell.v1.DeletionOutcome" json:"outcome,omitempty"` - // Immutable identity of the targeted sandbox, empty for ALREADY_ABSENT. - // A same-name replacement is not part of this deletion. - SandboxId string `protobuf:"bytes,3,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteSandboxResponse) Reset() { - *x = DeleteSandboxResponse{} - mi := &file_openshell_proto_msgTypes[71] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteSandboxResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteSandboxResponse) ProtoMessage() {} - -func (x *DeleteSandboxResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[71] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteSandboxResponse.ProtoReflect.Descriptor instead. -func (*DeleteSandboxResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{71} -} - -func (x *DeleteSandboxResponse) GetOutcome() DeletionOutcome { - if x != nil { - return x.Outcome - } - return DeletionOutcome_DELETION_OUTCOME_UNSPECIFIED -} - -func (x *DeleteSandboxResponse) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -// Create SSH session request. -type CreateSshSessionRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateSshSessionRequest) Reset() { - *x = CreateSshSessionRequest{} - mi := &file_openshell_proto_msgTypes[72] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateSshSessionRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateSshSessionRequest) ProtoMessage() {} - -func (x *CreateSshSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[72] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateSshSessionRequest.ProtoReflect.Descriptor instead. -func (*CreateSshSessionRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{72} -} - -func (x *CreateSshSessionRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *CreateSshSessionRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -// Create SSH session response. -// -// Fields are interpolated into an SSH `ProxyCommand` string that OpenSSH -// executes through `/bin/sh -c` on the caller's workstation. Servers MUST -// uphold the charset contract below; clients MUST reject responses that -// violate it. The client's own escaping provides defense-in-depth, but -// narrow charsets close injection vectors at the trust boundary. -type CreateSshSessionResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox id. [A-Za-z0-9._-]{1,128}. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Session token for the gateway tunnel. URL-safe ASCII - // ([A-Za-z0-9._~+/=-]) up to 4096 bytes. No shell metacharacters or - // whitespace. - Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` - // Gateway host for SSH proxy connection. IPv4 address, bracketed IPv6 - // address, or DNS hostname (Punycode-encoded for IDN). Alphanumeric plus - // `.-:[]` only, up to 253 bytes. - GatewayHost string `protobuf:"bytes,3,opt,name=gateway_host,json=gatewayHost,proto3" json:"gateway_host,omitempty"` - // Gateway port for SSH proxy connection. Must be in range 1..=65535. - GatewayPort uint32 `protobuf:"varint,4,opt,name=gateway_port,json=gatewayPort,proto3" json:"gateway_port,omitempty"` - // Gateway scheme. Must be exactly "http" or "https". - GatewayScheme string `protobuf:"bytes,5,opt,name=gateway_scheme,json=gatewayScheme,proto3" json:"gateway_scheme,omitempty"` - // Expected sandbox SSH host identity in OpenSSH SHA256: form. - // Stable for the sandbox's lifetime. Empty only for legacy runtimes. - HostKeyFingerprint string `protobuf:"bytes,7,opt,name=host_key_fingerprint,json=hostKeyFingerprint,proto3" json:"host_key_fingerprint,omitempty"` - // Absolute expiry. Absence means no expiry. - ExpirationTime *timestamppb.Timestamp `protobuf:"bytes,108,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateSshSessionResponse) Reset() { - *x = CreateSshSessionResponse{} - mi := &file_openshell_proto_msgTypes[73] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateSshSessionResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateSshSessionResponse) ProtoMessage() {} - -func (x *CreateSshSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[73] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateSshSessionResponse.ProtoReflect.Descriptor instead. -func (*CreateSshSessionResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{73} -} - -func (x *CreateSshSessionResponse) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *CreateSshSessionResponse) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -func (x *CreateSshSessionResponse) GetGatewayHost() string { - if x != nil { - return x.GatewayHost - } - return "" -} - -func (x *CreateSshSessionResponse) GetGatewayPort() uint32 { - if x != nil { - return x.GatewayPort - } - return 0 -} - -func (x *CreateSshSessionResponse) GetGatewayScheme() string { - if x != nil { - return x.GatewayScheme - } - return "" -} - -func (x *CreateSshSessionResponse) GetHostKeyFingerprint() string { - if x != nil { - return x.HostKeyFingerprint - } - return "" -} - -func (x *CreateSshSessionResponse) GetExpirationTime() *timestamppb.Timestamp { - if x != nil { - return x.ExpirationTime - } - return nil -} - -// Request to expose an HTTP service running inside a sandbox. -type ExposeServiceRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,5,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Service name within the sandbox. - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // Loopback TCP port inside the sandbox. - TargetPort uint32 `protobuf:"varint,3,opt,name=target_port,json=targetPort,proto3" json:"target_port,omitempty"` - // Whether to print/use the browser-facing service URL. - Domain bool `protobuf:"varint,4,opt,name=domain,proto3" json:"domain,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,6,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - // Application authorization behavior. Omission resolves to STRIP. - AuthorizationMode ServiceAuthorizationMode `protobuf:"varint,7,opt,name=authorization_mode,json=authorizationMode,proto3,enum=openshell.v1.ServiceAuthorizationMode" json:"authorization_mode,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExposeServiceRequest) Reset() { - *x = ExposeServiceRequest{} - mi := &file_openshell_proto_msgTypes[74] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExposeServiceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExposeServiceRequest) ProtoMessage() {} - -func (x *ExposeServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[74] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExposeServiceRequest.ProtoReflect.Descriptor instead. -func (*ExposeServiceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{74} -} - -func (x *ExposeServiceRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ExposeServiceRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ExposeServiceRequest) GetTargetPort() uint32 { - if x != nil { - return x.TargetPort - } - return 0 -} - -func (x *ExposeServiceRequest) GetDomain() bool { - if x != nil { - return x.Domain - } - return false -} - -func (x *ExposeServiceRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *ExposeServiceRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -func (x *ExposeServiceRequest) GetAuthorizationMode() ServiceAuthorizationMode { - if x != nil { - return x.AuthorizationMode - } - return ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_UNSPECIFIED -} - -// Request to fetch an exposed sandbox service endpoint. -type GetServiceRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Service name within the sandbox. Empty selects the unnamed endpoint. - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetServiceRequest) Reset() { - *x = GetServiceRequest{} - mi := &file_openshell_proto_msgTypes[75] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetServiceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetServiceRequest) ProtoMessage() {} - -func (x *GetServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[75] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetServiceRequest.ProtoReflect.Descriptor instead. -func (*GetServiceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{75} -} - -func (x *GetServiceRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetServiceRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *GetServiceRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -// Request to list exposed sandbox service endpoints. -type ListServicesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Named and all-workspaces selections are accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,4,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // The maximum number of services to return. Zero uses 100. Values above - // 1000 are coerced to 1000; negative values are invalid. - PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - // Token from a previous ListServices response. All other request parameters - // except page_size must match the request that produced it. - PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` - // Optional sandbox name. Empty lists endpoints for all sandboxes. - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListServicesRequest) Reset() { - *x = ListServicesRequest{} - mi := &file_openshell_proto_msgTypes[76] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListServicesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListServicesRequest) ProtoMessage() {} - -func (x *ListServicesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[76] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListServicesRequest.ProtoReflect.Descriptor instead. -func (*ListServicesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{76} -} - -func (x *ListServicesRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ListServicesRequest) GetPageSize() int32 { - if x != nil { - return x.PageSize - } - return 0 -} - -func (x *ListServicesRequest) GetPageToken() string { - if x != nil { - return x.PageToken - } - return "" -} - -func (x *ListServicesRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -// Response containing exposed sandbox service endpoints. -type ListServicesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Services []*ServiceEndpointResponse `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"` - // Token for the next page. Empty when there are no subsequent pages. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListServicesResponse) Reset() { - *x = ListServicesResponse{} - mi := &file_openshell_proto_msgTypes[77] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListServicesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListServicesResponse) ProtoMessage() {} - -func (x *ListServicesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[77] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListServicesResponse.ProtoReflect.Descriptor instead. -func (*ListServicesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{77} -} - -func (x *ListServicesResponse) GetServices() []*ServiceEndpointResponse { - if x != nil { - return x.Services - } - return nil -} - -func (x *ListServicesResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -// Request to delete an exposed sandbox service endpoint. -type DeleteServiceRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Service name within the sandbox. Empty selects the unnamed endpoint. - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - AllowMissing bool `protobuf:"varint,4,opt,name=allow_missing,json=allowMissing,proto3" json:"allow_missing,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteServiceRequest) Reset() { - *x = DeleteServiceRequest{} - mi := &file_openshell_proto_msgTypes[78] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteServiceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteServiceRequest) ProtoMessage() {} - -func (x *DeleteServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[78] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteServiceRequest.ProtoReflect.Descriptor instead. -func (*DeleteServiceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{78} -} - -func (x *DeleteServiceRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *DeleteServiceRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *DeleteServiceRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *DeleteServiceRequest) GetAllowMissing() bool { - if x != nil { - return x.AllowMissing - } - return false -} - -func (x *DeleteServiceRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Response for deleting an exposed sandbox service endpoint. -type DeleteServiceResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Outcome DeletionOutcome `protobuf:"varint,2,opt,name=outcome,proto3,enum=openshell.v1.DeletionOutcome" json:"outcome,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteServiceResponse) Reset() { - *x = DeleteServiceResponse{} - mi := &file_openshell_proto_msgTypes[79] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteServiceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteServiceResponse) ProtoMessage() {} - -func (x *DeleteServiceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[79] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteServiceResponse.ProtoReflect.Descriptor instead. -func (*DeleteServiceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{79} -} - -func (x *DeleteServiceResponse) GetOutcome() DeletionOutcome { - if x != nil { - return x.Outcome - } - return DeletionOutcome_DELETION_OUTCOME_UNSPECIFIED -} - -// Persisted sandbox service endpoint. -type ServiceEndpoint struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Kubernetes-style metadata. - Metadata *datamodelv1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - // Sandbox object ID. - SandboxId string `protobuf:"bytes,2,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Sandbox name. - Sandbox string `protobuf:"bytes,3,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Service name within the sandbox. - Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` - // Loopback TCP port inside the sandbox. - TargetPort uint32 `protobuf:"varint,5,opt,name=target_port,json=targetPort,proto3" json:"target_port,omitempty"` - // Whether browser-facing service routing is enabled for this endpoint. - Domain bool `protobuf:"varint,6,opt,name=domain,proto3" json:"domain,omitempty"` - // Effective application authorization behavior for ingress requests. - AuthorizationMode ServiceAuthorizationMode `protobuf:"varint,7,opt,name=authorization_mode,json=authorizationMode,proto3,enum=openshell.v1.ServiceAuthorizationMode" json:"authorization_mode,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ServiceEndpoint) Reset() { - *x = ServiceEndpoint{} - mi := &file_openshell_proto_msgTypes[80] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ServiceEndpoint) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ServiceEndpoint) ProtoMessage() {} - -func (x *ServiceEndpoint) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[80] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ServiceEndpoint.ProtoReflect.Descriptor instead. -func (*ServiceEndpoint) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{80} -} - -func (x *ServiceEndpoint) GetMetadata() *datamodelv1.ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *ServiceEndpoint) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *ServiceEndpoint) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *ServiceEndpoint) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ServiceEndpoint) GetTargetPort() uint32 { - if x != nil { - return x.TargetPort - } - return 0 -} - -func (x *ServiceEndpoint) GetDomain() bool { - if x != nil { - return x.Domain - } - return false -} - -func (x *ServiceEndpoint) GetAuthorizationMode() ServiceAuthorizationMode { - if x != nil { - return x.AuthorizationMode - } - return ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_UNSPECIFIED -} - -// Response containing a service endpoint and, when available, its local URL. -type ServiceEndpointResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Endpoint *ServiceEndpoint `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"endpoint,omitempty"` - Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ServiceEndpointResponse) Reset() { - *x = ServiceEndpointResponse{} - mi := &file_openshell_proto_msgTypes[81] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ServiceEndpointResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ServiceEndpointResponse) ProtoMessage() {} - -func (x *ServiceEndpointResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[81] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ServiceEndpointResponse.ProtoReflect.Descriptor instead. -func (*ServiceEndpointResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{81} -} - -func (x *ServiceEndpointResponse) GetEndpoint() *ServiceEndpoint { - if x != nil { - return x.Endpoint - } - return nil -} - -func (x *ServiceEndpointResponse) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -// Revoke SSH session request. -type RevokeSshSessionRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Session token to revoke. - Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` - // A missing token is NOT_FOUND unless this is true. Revoking an existing, - // already-revoked session succeeds with COMPLETED. - AllowMissing bool `protobuf:"varint,2,opt,name=allow_missing,json=allowMissing,proto3" json:"allow_missing,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RevokeSshSessionRequest) Reset() { - *x = RevokeSshSessionRequest{} - mi := &file_openshell_proto_msgTypes[82] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RevokeSshSessionRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RevokeSshSessionRequest) ProtoMessage() {} - -func (x *RevokeSshSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[82] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RevokeSshSessionRequest.ProtoReflect.Descriptor instead. -func (*RevokeSshSessionRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{82} -} - -func (x *RevokeSshSessionRequest) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -func (x *RevokeSshSessionRequest) GetAllowMissing() bool { - if x != nil { - return x.AllowMissing - } - return false -} - -// Revoke SSH session response. -type RevokeSshSessionResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Outcome DeletionOutcome `protobuf:"varint,2,opt,name=outcome,proto3,enum=openshell.v1.DeletionOutcome" json:"outcome,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RevokeSshSessionResponse) Reset() { - *x = RevokeSshSessionResponse{} - mi := &file_openshell_proto_msgTypes[83] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RevokeSshSessionResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RevokeSshSessionResponse) ProtoMessage() {} - -func (x *RevokeSshSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[83] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RevokeSshSessionResponse.ProtoReflect.Descriptor instead. -func (*RevokeSshSessionResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{83} -} - -func (x *RevokeSshSessionResponse) GetOutcome() DeletionOutcome { - if x != nil { - return x.Outcome - } - return DeletionOutcome_DELETION_OUTCOME_UNSPECIFIED -} - -// Execute command request. -type ExecSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,12,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Canonical sandbox name. - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Command and arguments. - Command []string `protobuf:"bytes,2,rep,name=command,proto3" json:"command,omitempty"` - // Optional working directory. - Workdir string `protobuf:"bytes,3,opt,name=workdir,proto3" json:"workdir,omitempty"` - // Optional environment overrides. - Environment map[string]string `protobuf:"bytes,4,rep,name=environment,proto3" json:"environment,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Optional execution timeout. Absence means no timeout. - ExecutionTimeout *durationpb.Duration `protobuf:"bytes,105,opt,name=execution_timeout,json=executionTimeout,proto3" json:"execution_timeout,omitempty"` - // Optional stdin payload passed to the command. - Stdin []byte `protobuf:"bytes,6,opt,name=stdin,proto3" json:"stdin,omitempty"` - // Request a pseudo-terminal for the remote command. - Tty bool `protobuf:"varint,7,opt,name=tty,proto3" json:"tty,omitempty"` - // Initial terminal columns (used when tty=true, 0 = use default). - Cols uint32 `protobuf:"varint,8,opt,name=cols,proto3" json:"cols,omitempty"` - // Initial terminal rows (used when tty=true, 0 = use default). - Rows uint32 `protobuf:"varint,9,opt,name=rows,proto3" json:"rows,omitempty"` - // Skip sourcing shell login/profile startup files before running the command. - // When false (the default), the command runs through a login shell - // (`bash -lc`) so user startup files (.bash_profile/.profile, and .bashrc if - // sourced by them) are applied. When true, the command runs without those - // files (`bash -c`), for automation that needs predictable startup behavior. - NoLoginShell bool `protobuf:"varint,10,opt,name=no_login_shell,json=noLoginShell,proto3" json:"no_login_shell,omitempty"` - // Optional nonzero UUID for durable launch admission. Also applies to the - // initial ExecSandboxInteractive start message. Duplicates never relaunch, - // reattach, or replay output/stdin. Unconfirmed executions remain fenced; - // confirmed terminal executions retain the fence for 24 hours. - RequestId string `protobuf:"bytes,11,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxRequest) Reset() { - *x = ExecSandboxRequest{} - mi := &file_openshell_proto_msgTypes[84] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxRequest) ProtoMessage() {} - -func (x *ExecSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[84] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxRequest.ProtoReflect.Descriptor instead. -func (*ExecSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{84} -} - -func (x *ExecSandboxRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ExecSandboxRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *ExecSandboxRequest) GetCommand() []string { - if x != nil { - return x.Command - } - return nil -} - -func (x *ExecSandboxRequest) GetWorkdir() string { - if x != nil { - return x.Workdir - } - return "" -} - -func (x *ExecSandboxRequest) GetEnvironment() map[string]string { - if x != nil { - return x.Environment - } - return nil -} - -func (x *ExecSandboxRequest) GetExecutionTimeout() *durationpb.Duration { - if x != nil { - return x.ExecutionTimeout - } - return nil -} - -func (x *ExecSandboxRequest) GetStdin() []byte { - if x != nil { - return x.Stdin - } - return nil -} - -func (x *ExecSandboxRequest) GetTty() bool { - if x != nil { - return x.Tty - } - return false -} - -func (x *ExecSandboxRequest) GetCols() uint32 { - if x != nil { - return x.Cols - } - return 0 -} - -func (x *ExecSandboxRequest) GetRows() uint32 { - if x != nil { - return x.Rows - } - return 0 -} - -func (x *ExecSandboxRequest) GetNoLoginShell() bool { - if x != nil { - return x.NoLoginShell - } - return false -} - -func (x *ExecSandboxRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// One stdout chunk from a sandbox exec. -type ExecSandboxStdout struct { - state protoimpl.MessageState `protogen:"open.v1"` - Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxStdout) Reset() { - *x = ExecSandboxStdout{} - mi := &file_openshell_proto_msgTypes[85] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxStdout) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxStdout) ProtoMessage() {} - -func (x *ExecSandboxStdout) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[85] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxStdout.ProtoReflect.Descriptor instead. -func (*ExecSandboxStdout) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{85} -} - -func (x *ExecSandboxStdout) GetData() []byte { - if x != nil { - return x.Data - } - return nil -} - -// One stderr chunk from a sandbox exec. -type ExecSandboxStderr struct { - state protoimpl.MessageState `protogen:"open.v1"` - Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxStderr) Reset() { - *x = ExecSandboxStderr{} - mi := &file_openshell_proto_msgTypes[86] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxStderr) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxStderr) ProtoMessage() {} - -func (x *ExecSandboxStderr) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[86] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxStderr.ProtoReflect.Descriptor instead. -func (*ExecSandboxStderr) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{86} -} - -func (x *ExecSandboxStderr) GetData() []byte { - if x != nil { - return x.Data - } - return nil -} - -// Final exit status for a sandbox exec. -type ExecSandboxExit struct { - state protoimpl.MessageState `protogen:"open.v1"` - ExitCode int32 `protobuf:"varint,1,opt,name=exit_code,json=exitCode,proto3" json:"exit_code,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxExit) Reset() { - *x = ExecSandboxExit{} - mi := &file_openshell_proto_msgTypes[87] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxExit) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxExit) ProtoMessage() {} - -func (x *ExecSandboxExit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[87] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxExit.ProtoReflect.Descriptor instead. -func (*ExecSandboxExit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{87} -} - -func (x *ExecSandboxExit) GetExitCode() int32 { - if x != nil { - return x.ExitCode - } - return 0 -} - -// One event in a sandbox exec stream. -type ExecSandboxEvent struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *ExecSandboxEvent_Stdout - // *ExecSandboxEvent_Stderr - // *ExecSandboxEvent_Exit - Payload isExecSandboxEvent_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxEvent) Reset() { - *x = ExecSandboxEvent{} - mi := &file_openshell_proto_msgTypes[88] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxEvent) ProtoMessage() {} - -func (x *ExecSandboxEvent) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[88] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxEvent.ProtoReflect.Descriptor instead. -func (*ExecSandboxEvent) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{88} -} - -func (x *ExecSandboxEvent) GetPayload() isExecSandboxEvent_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *ExecSandboxEvent) GetStdout() *ExecSandboxStdout { - if x != nil { - if x, ok := x.Payload.(*ExecSandboxEvent_Stdout); ok { - return x.Stdout - } - } - return nil -} - -func (x *ExecSandboxEvent) GetStderr() *ExecSandboxStderr { - if x != nil { - if x, ok := x.Payload.(*ExecSandboxEvent_Stderr); ok { - return x.Stderr - } - } - return nil -} - -func (x *ExecSandboxEvent) GetExit() *ExecSandboxExit { - if x != nil { - if x, ok := x.Payload.(*ExecSandboxEvent_Exit); ok { - return x.Exit - } - } - return nil -} - -type isExecSandboxEvent_Payload interface { - isExecSandboxEvent_Payload() -} - -type ExecSandboxEvent_Stdout struct { - Stdout *ExecSandboxStdout `protobuf:"bytes,1,opt,name=stdout,proto3,oneof"` -} - -type ExecSandboxEvent_Stderr struct { - Stderr *ExecSandboxStderr `protobuf:"bytes,2,opt,name=stderr,proto3,oneof"` -} - -type ExecSandboxEvent_Exit struct { - Exit *ExecSandboxExit `protobuf:"bytes,3,opt,name=exit,proto3,oneof"` -} - -func (*ExecSandboxEvent_Stdout) isExecSandboxEvent_Payload() {} - -func (*ExecSandboxEvent_Stderr) isExecSandboxEvent_Payload() {} - -func (*ExecSandboxEvent_Exit) isExecSandboxEvent_Payload() {} - -// Initial frame for one TCP forward stream. -type TcpForwardInit struct { - state protoimpl.MessageState `protogen:"open.v1"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - Workspace string `protobuf:"bytes,3,opt,name=workspace,proto3" json:"workspace,omitempty"` - // Optional service identifier for audit/correlation. - ServiceId string `protobuf:"bytes,4,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` - // Target the gateway should request from the supervisor. - // - // Types that are valid to be assigned to Target: - // - // *TcpForwardInit_Ssh - // *TcpForwardInit_Tcp - Target isTcpForwardInit_Target `protobuf_oneof:"target"` - // Optional target-specific authorization token. SSH targets use this as the - // short-lived SSH session token issued by CreateSshSession. - AuthorizationToken string `protobuf:"bytes,7,opt,name=authorization_token,json=authorizationToken,proto3" json:"authorization_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TcpForwardInit) Reset() { - *x = TcpForwardInit{} - mi := &file_openshell_proto_msgTypes[89] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TcpForwardInit) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TcpForwardInit) ProtoMessage() {} - -func (x *TcpForwardInit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[89] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TcpForwardInit.ProtoReflect.Descriptor instead. -func (*TcpForwardInit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{89} -} - -func (x *TcpForwardInit) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *TcpForwardInit) GetWorkspace() string { - if x != nil { - return x.Workspace - } - return "" -} - -func (x *TcpForwardInit) GetServiceId() string { - if x != nil { - return x.ServiceId - } - return "" -} - -func (x *TcpForwardInit) GetTarget() isTcpForwardInit_Target { - if x != nil { - return x.Target - } - return nil -} - -func (x *TcpForwardInit) GetSsh() *SshRelayTarget { - if x != nil { - if x, ok := x.Target.(*TcpForwardInit_Ssh); ok { - return x.Ssh - } - } - return nil -} - -func (x *TcpForwardInit) GetTcp() *TcpRelayTarget { - if x != nil { - if x, ok := x.Target.(*TcpForwardInit_Tcp); ok { - return x.Tcp - } - } - return nil -} - -func (x *TcpForwardInit) GetAuthorizationToken() string { - if x != nil { - return x.AuthorizationToken - } - return "" -} - -type isTcpForwardInit_Target interface { - isTcpForwardInit_Target() -} - -type TcpForwardInit_Ssh struct { - Ssh *SshRelayTarget `protobuf:"bytes,5,opt,name=ssh,proto3,oneof"` -} - -type TcpForwardInit_Tcp struct { - Tcp *TcpRelayTarget `protobuf:"bytes,6,opt,name=tcp,proto3,oneof"` -} - -func (*TcpForwardInit_Ssh) isTcpForwardInit_Target() {} - -func (*TcpForwardInit_Tcp) isTcpForwardInit_Target() {} - -// A single frame on the CLI-to-gateway TCP forward stream. -type TcpForwardFrame struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *TcpForwardFrame_Init - // *TcpForwardFrame_Data - Payload isTcpForwardFrame_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TcpForwardFrame) Reset() { - *x = TcpForwardFrame{} - mi := &file_openshell_proto_msgTypes[90] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TcpForwardFrame) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TcpForwardFrame) ProtoMessage() {} - -func (x *TcpForwardFrame) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[90] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TcpForwardFrame.ProtoReflect.Descriptor instead. -func (*TcpForwardFrame) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{90} -} - -func (x *TcpForwardFrame) GetPayload() isTcpForwardFrame_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *TcpForwardFrame) GetInit() *TcpForwardInit { - if x != nil { - if x, ok := x.Payload.(*TcpForwardFrame_Init); ok { - return x.Init - } - } - return nil -} - -func (x *TcpForwardFrame) GetData() []byte { - if x != nil { - if x, ok := x.Payload.(*TcpForwardFrame_Data); ok { - return x.Data - } - } - return nil -} - -type isTcpForwardFrame_Payload interface { - isTcpForwardFrame_Payload() -} - -type TcpForwardFrame_Init struct { - Init *TcpForwardInit `protobuf:"bytes,1,opt,name=init,proto3,oneof"` -} - -type TcpForwardFrame_Data struct { - Data []byte `protobuf:"bytes,2,opt,name=data,proto3,oneof"` -} - -func (*TcpForwardFrame_Init) isTcpForwardFrame_Payload() {} - -func (*TcpForwardFrame_Data) isTcpForwardFrame_Payload() {} - -// Client-to-server message for interactive exec. -type ExecSandboxInput struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *ExecSandboxInput_Start - // *ExecSandboxInput_Stdin - // *ExecSandboxInput_Resize - Payload isExecSandboxInput_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxInput) Reset() { - *x = ExecSandboxInput{} - mi := &file_openshell_proto_msgTypes[91] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxInput) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxInput) ProtoMessage() {} - -func (x *ExecSandboxInput) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[91] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxInput.ProtoReflect.Descriptor instead. -func (*ExecSandboxInput) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{91} -} - -func (x *ExecSandboxInput) GetPayload() isExecSandboxInput_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *ExecSandboxInput) GetStart() *ExecSandboxRequest { - if x != nil { - if x, ok := x.Payload.(*ExecSandboxInput_Start); ok { - return x.Start - } - } - return nil -} - -func (x *ExecSandboxInput) GetStdin() []byte { - if x != nil { - if x, ok := x.Payload.(*ExecSandboxInput_Stdin); ok { - return x.Stdin - } - } - return nil -} - -func (x *ExecSandboxInput) GetResize() *ExecSandboxWindowResize { - if x != nil { - if x, ok := x.Payload.(*ExecSandboxInput_Resize); ok { - return x.Resize - } - } - return nil -} - -type isExecSandboxInput_Payload interface { - isExecSandboxInput_Payload() -} - -type ExecSandboxInput_Start struct { - // First message: exec request metadata. - Start *ExecSandboxRequest `protobuf:"bytes,1,opt,name=start,proto3,oneof"` -} - -type ExecSandboxInput_Stdin struct { - // Subsequent messages: raw stdin bytes. - Stdin []byte `protobuf:"bytes,2,opt,name=stdin,proto3,oneof"` -} - -type ExecSandboxInput_Resize struct { - // Terminal window size change. - Resize *ExecSandboxWindowResize `protobuf:"bytes,3,opt,name=resize,proto3,oneof"` -} - -func (*ExecSandboxInput_Start) isExecSandboxInput_Payload() {} - -func (*ExecSandboxInput_Stdin) isExecSandboxInput_Payload() {} - -func (*ExecSandboxInput_Resize) isExecSandboxInput_Payload() {} - -// Terminal window resize event for interactive exec. -type ExecSandboxWindowResize struct { - state protoimpl.MessageState `protogen:"open.v1"` - Cols uint32 `protobuf:"varint,1,opt,name=cols,proto3" json:"cols,omitempty"` - Rows uint32 `protobuf:"varint,2,opt,name=rows,proto3" json:"rows,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxWindowResize) Reset() { - *x = ExecSandboxWindowResize{} - mi := &file_openshell_proto_msgTypes[92] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxWindowResize) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxWindowResize) ProtoMessage() {} - -func (x *ExecSandboxWindowResize) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[92] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxWindowResize.ProtoReflect.Descriptor instead. -func (*ExecSandboxWindowResize) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{92} -} - -func (x *ExecSandboxWindowResize) GetCols() uint32 { - if x != nil { - return x.Cols - } - return 0 -} - -func (x *ExecSandboxWindowResize) GetRows() uint32 { - if x != nil { - return x.Rows - } - return 0 -} - -// SSH session record stored in persistence. -type SshSession struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Kubernetes-style metadata (id, name, labels, timestamps, resource version). - Metadata *datamodelv1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - // Sandbox id. - SandboxId string `protobuf:"bytes,2,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Session token. - Token string `protobuf:"bytes,3,opt,name=token,proto3" json:"token,omitempty"` - // Absolute expiry. Absence means no expiry. - ExpirationTime *timestamppb.Timestamp `protobuf:"bytes,104,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` - // Revoked flag. - Revoked bool `protobuf:"varint,5,opt,name=revoked,proto3" json:"revoked,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SshSession) Reset() { - *x = SshSession{} - mi := &file_openshell_proto_msgTypes[93] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SshSession) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SshSession) ProtoMessage() {} - -func (x *SshSession) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[93] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SshSession.ProtoReflect.Descriptor instead. -func (*SshSession) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{93} -} - -func (x *SshSession) GetMetadata() *datamodelv1.ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *SshSession) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *SshSession) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -func (x *SshSession) GetExpirationTime() *timestamppb.Timestamp { - if x != nil { - return x.ExpirationTime - } - return nil -} - -func (x *SshSession) GetRevoked() bool { - if x != nil { - return x.Revoked - } - return false -} - -// Watch sandbox request. -type WatchSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,11,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Canonical sandbox name. - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Stream sandbox status snapshots. - FollowStatus bool `protobuf:"varint,2,opt,name=follow_status,json=followStatus,proto3" json:"follow_status,omitempty"` - // Stream openshell-server process logs correlated to this sandbox. - FollowLogs bool `protobuf:"varint,3,opt,name=follow_logs,json=followLogs,proto3" json:"follow_logs,omitempty"` - // Stream platform events correlated to this sandbox. - FollowEvents bool `protobuf:"varint,4,opt,name=follow_events,json=followEvents,proto3" json:"follow_events,omitempty"` - // Replay the last N log lines (best-effort) before following. - LogTailLines uint32 `protobuf:"varint,5,opt,name=log_tail_lines,json=logTailLines,proto3" json:"log_tail_lines,omitempty"` - // Replay the last N platform events (best-effort) before following. - EventTail uint32 `protobuf:"varint,6,opt,name=event_tail,json=eventTail,proto3" json:"event_tail,omitempty"` - // Stop streaming once the sandbox reaches READY or a terminal result phase - // (COMPLETED, STOPPED, or ERROR). - StopOnTerminal bool `protobuf:"varint,7,opt,name=stop_on_terminal,json=stopOnTerminal,proto3" json:"stop_on_terminal,omitempty"` - // Only include log lines at or after this time. Absence means no time filter. - // Applies to both tail replay and live streaming. - SinceTime *timestamppb.Timestamp `protobuf:"bytes,108,opt,name=since_time,json=sinceTime,proto3" json:"since_time,omitempty"` - // Filter by log source (e.g. "gateway", "sandbox"). Empty means all sources. - LogSources []string `protobuf:"bytes,9,rep,name=log_sources,json=logSources,proto3" json:"log_sources,omitempty"` - // Minimum log level to include (e.g. "INFO", "WARN", "ERROR"). Empty means all levels. - LogMinLevel string `protobuf:"bytes,10,opt,name=log_min_level,json=logMinLevel,proto3" json:"log_min_level,omitempty"` - // Resume streaming after this cursor. Empty means no cursor resume: the - // server falls back to tail-limited replay controlled by log_tail_lines and - // event_tail. Otherwise set it to the highest `SandboxStreamEvent.cursor` - // already processed; the server replays only log and platform events after - // it, merged in cursor order, before resuming live delivery. If the requested - // cursor has already been trimmed from the server's buffer, the resume is - // unrecoverable and the stream terminates with OUT_OF_RANGE (see - // SandboxStreamWarning for the recoverable case). - // - // A cursor is bound to the cursor space that issued it. A gateway restart, - // teardown of the sandbox's buffers, or a reconnect to a different gateway - // replica starts a new space, and cursors from the previous one are rejected - // with OUT_OF_RANGE rather than silently suppressing live events beneath - // them. OUT_OF_RANGE is terminal for that cursor: restart the watch with an - // empty resume_after_cursor, because retrying the same token fails - // identically. A cursor this server could not have issued is rejected with - // INVALID_ARGUMENT. - ResumeAfterCursor string `protobuf:"bytes,12,opt,name=resume_after_cursor,json=resumeAfterCursor,proto3" json:"resume_after_cursor,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WatchSandboxRequest) Reset() { - *x = WatchSandboxRequest{} - mi := &file_openshell_proto_msgTypes[94] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WatchSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WatchSandboxRequest) ProtoMessage() {} - -func (x *WatchSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[94] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WatchSandboxRequest.ProtoReflect.Descriptor instead. -func (*WatchSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{94} -} - -func (x *WatchSandboxRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *WatchSandboxRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *WatchSandboxRequest) GetFollowStatus() bool { - if x != nil { - return x.FollowStatus - } - return false -} - -func (x *WatchSandboxRequest) GetFollowLogs() bool { - if x != nil { - return x.FollowLogs - } - return false -} - -func (x *WatchSandboxRequest) GetFollowEvents() bool { - if x != nil { - return x.FollowEvents - } - return false -} - -func (x *WatchSandboxRequest) GetLogTailLines() uint32 { - if x != nil { - return x.LogTailLines - } - return 0 -} - -func (x *WatchSandboxRequest) GetEventTail() uint32 { - if x != nil { - return x.EventTail - } - return 0 -} - -func (x *WatchSandboxRequest) GetStopOnTerminal() bool { - if x != nil { - return x.StopOnTerminal - } - return false -} - -func (x *WatchSandboxRequest) GetSinceTime() *timestamppb.Timestamp { - if x != nil { - return x.SinceTime - } - return nil -} - -func (x *WatchSandboxRequest) GetLogSources() []string { - if x != nil { - return x.LogSources - } - return nil -} - -func (x *WatchSandboxRequest) GetLogMinLevel() string { - if x != nil { - return x.LogMinLevel - } - return "" -} - -func (x *WatchSandboxRequest) GetResumeAfterCursor() string { - if x != nil { - return x.ResumeAfterCursor - } - return "" -} - -// One event in a sandbox watch stream. -type SandboxStreamEvent struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *SandboxStreamEvent_Sandbox - // *SandboxStreamEvent_Log - // *SandboxStreamEvent_Event - // *SandboxStreamEvent_Warning - // *SandboxStreamEvent_DraftPolicyUpdate - Payload isSandboxStreamEvent_Payload `protobuf_oneof:"payload"` - // Opaque position in this sandbox's cursor space, shared across the resumable - // log and platform event sources. Empty for non-resumable events (status - // snapshots, warnings). - // - // Do not parse this token; its encoding is not part of the contract. The only - // supported operation is comparing two non-empty cursors observed on the same - // stream and keeping the greater one, then passing it as - // WatchSandboxRequest.resume_after_cursor to resume without loss or - // duplication. That comparison is a plain byte-wise string comparison. It is - // well defined only within one stream: a stream never spans two cursor - // spaces, because a reset ends it. - Cursor string `protobuf:"bytes,6,opt,name=cursor,proto3" json:"cursor,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxStreamEvent) Reset() { - *x = SandboxStreamEvent{} - mi := &file_openshell_proto_msgTypes[95] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxStreamEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxStreamEvent) ProtoMessage() {} - -func (x *SandboxStreamEvent) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[95] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxStreamEvent.ProtoReflect.Descriptor instead. -func (*SandboxStreamEvent) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{95} -} - -func (x *SandboxStreamEvent) GetPayload() isSandboxStreamEvent_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *SandboxStreamEvent) GetSandbox() *Sandbox { - if x != nil { - if x, ok := x.Payload.(*SandboxStreamEvent_Sandbox); ok { - return x.Sandbox - } - } - return nil -} - -func (x *SandboxStreamEvent) GetLog() *SandboxLogLine { - if x != nil { - if x, ok := x.Payload.(*SandboxStreamEvent_Log); ok { - return x.Log - } - } - return nil -} - -func (x *SandboxStreamEvent) GetEvent() *PlatformEvent { - if x != nil { - if x, ok := x.Payload.(*SandboxStreamEvent_Event); ok { - return x.Event - } - } - return nil -} - -func (x *SandboxStreamEvent) GetWarning() *SandboxStreamWarning { - if x != nil { - if x, ok := x.Payload.(*SandboxStreamEvent_Warning); ok { - return x.Warning - } - } - return nil -} - -func (x *SandboxStreamEvent) GetDraftPolicyUpdate() *DraftPolicyUpdate { - if x != nil { - if x, ok := x.Payload.(*SandboxStreamEvent_DraftPolicyUpdate); ok { - return x.DraftPolicyUpdate - } - } - return nil -} - -func (x *SandboxStreamEvent) GetCursor() string { - if x != nil { - return x.Cursor - } - return "" -} - -type isSandboxStreamEvent_Payload interface { - isSandboxStreamEvent_Payload() -} - -type SandboxStreamEvent_Sandbox struct { - // Latest sandbox snapshot. - Sandbox *Sandbox `protobuf:"bytes,1,opt,name=sandbox,proto3,oneof"` -} - -type SandboxStreamEvent_Log struct { - // One server log line/event. - Log *SandboxLogLine `protobuf:"bytes,2,opt,name=log,proto3,oneof"` -} - -type SandboxStreamEvent_Event struct { - // One platform event. - Event *PlatformEvent `protobuf:"bytes,3,opt,name=event,proto3,oneof"` -} - -type SandboxStreamEvent_Warning struct { - // Recoverable warning from the server, e.g. messages dropped because a - // broadcast receiver lagged. The stream continues after this warning; the - // client can detect the gap from the warning itself. - Warning *SandboxStreamWarning `protobuf:"bytes,4,opt,name=warning,proto3,oneof"` -} - -type SandboxStreamEvent_DraftPolicyUpdate struct { - // Draft policy update notification. - DraftPolicyUpdate *DraftPolicyUpdate `protobuf:"bytes,5,opt,name=draft_policy_update,json=draftPolicyUpdate,proto3,oneof"` -} - -func (*SandboxStreamEvent_Sandbox) isSandboxStreamEvent_Payload() {} - -func (*SandboxStreamEvent_Log) isSandboxStreamEvent_Payload() {} - -func (*SandboxStreamEvent_Event) isSandboxStreamEvent_Payload() {} - -func (*SandboxStreamEvent_Warning) isSandboxStreamEvent_Payload() {} - -func (*SandboxStreamEvent_DraftPolicyUpdate) isSandboxStreamEvent_Payload() {} - -// Log line correlated to a sandbox. -type SandboxLogLine struct { - state protoimpl.MessageState `protogen:"open.v1"` - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - EventTime *timestamppb.Timestamp `protobuf:"bytes,102,opt,name=event_time,json=eventTime,proto3" json:"event_time,omitempty"` - Level string `protobuf:"bytes,3,opt,name=level,proto3" json:"level,omitempty"` - Target string `protobuf:"bytes,4,opt,name=target,proto3" json:"target,omitempty"` - Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` - // Log source: "gateway" (server-side) or "sandbox" (supervisor). - // Empty is treated as "gateway" for backward compatibility. - Source string `protobuf:"bytes,6,opt,name=source,proto3" json:"source,omitempty"` - // Structured key-value fields from the tracing event (e.g. dst_host, action). - Fields map[string]string `protobuf:"bytes,7,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxLogLine) Reset() { - *x = SandboxLogLine{} - mi := &file_openshell_proto_msgTypes[96] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxLogLine) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxLogLine) ProtoMessage() {} - -func (x *SandboxLogLine) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[96] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxLogLine.ProtoReflect.Descriptor instead. -func (*SandboxLogLine) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{96} -} - -func (x *SandboxLogLine) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *SandboxLogLine) GetEventTime() *timestamppb.Timestamp { - if x != nil { - return x.EventTime - } - return nil -} - -func (x *SandboxLogLine) GetLevel() string { - if x != nil { - return x.Level - } - return "" -} - -func (x *SandboxLogLine) GetTarget() string { - if x != nil { - return x.Target - } - return "" -} - -func (x *SandboxLogLine) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *SandboxLogLine) GetSource() string { - if x != nil { - return x.Source - } - return "" -} - -func (x *SandboxLogLine) GetFields() map[string]string { - if x != nil { - return x.Fields - } - return nil -} - -// Recoverable loss notification on a watch stream. Emitted when the server -// skips ahead after a broadcast lag instead of terminating; the stream keeps -// running. Cursors are opaque, so this message is the only signal that events -// were skipped. Unrecoverable loss (a trimmed or foreign resume cursor) is -// reported as an OUT_OF_RANGE stream status, not this message. -type SandboxStreamWarning struct { - state protoimpl.MessageState `protogen:"open.v1"` - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxStreamWarning) Reset() { - *x = SandboxStreamWarning{} - mi := &file_openshell_proto_msgTypes[97] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxStreamWarning) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxStreamWarning) ProtoMessage() {} - -func (x *SandboxStreamWarning) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[97] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxStreamWarning.ProtoReflect.Descriptor instead. -func (*SandboxStreamWarning) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{97} -} - -func (x *SandboxStreamWarning) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -// Create provider request. -type CreateProviderRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Provider *datamodelv1.Provider `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateProviderRequest) Reset() { - *x = CreateProviderRequest{} - mi := &file_openshell_proto_msgTypes[98] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateProviderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateProviderRequest) ProtoMessage() {} - -func (x *CreateProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[98] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateProviderRequest.ProtoReflect.Descriptor instead. -func (*CreateProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{98} -} - -func (x *CreateProviderRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *CreateProviderRequest) GetProvider() *datamodelv1.Provider { - if x != nil { - return x.Provider - } - return nil -} - -func (x *CreateProviderRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Get provider request. -type GetProviderRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetProviderRequest) Reset() { - *x = GetProviderRequest{} - mi := &file_openshell_proto_msgTypes[99] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetProviderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetProviderRequest) ProtoMessage() {} - -func (x *GetProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[99] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetProviderRequest.ProtoReflect.Descriptor instead. -func (*GetProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{99} -} - -func (x *GetProviderRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetProviderRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// Retrieve selected runtime credentials from one provider in one workspace. -// Refresh side effects can persist even when the complete request fails. -type GetProviderCredentialsRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Required named workspace selection; all_workspaces is not accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,1,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Provider canonical name. - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // Nonempty selection of runtime environment keys (maximum 32). - CredentialKeys []string `protobuf:"bytes,3,rep,name=credential_keys,json=credentialKeys,proto3" json:"credential_keys,omitempty"` - // Omitted/zero uses a five-minute safety margin. Maximum is 24 hours. - MinimumRemainingLifetime *durationpb.Duration `protobuf:"bytes,4,opt,name=minimum_remaining_lifetime,json=minimumRemainingLifetime,proto3" json:"minimum_remaining_lifetime,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetProviderCredentialsRequest) Reset() { - *x = GetProviderCredentialsRequest{} - mi := &file_openshell_proto_msgTypes[100] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetProviderCredentialsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetProviderCredentialsRequest) ProtoMessage() {} - -func (x *GetProviderCredentialsRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[100] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetProviderCredentialsRequest.ProtoReflect.Descriptor instead. -func (*GetProviderCredentialsRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{100} -} - -func (x *GetProviderCredentialsRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetProviderCredentialsRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *GetProviderCredentialsRequest) GetCredentialKeys() []string { - if x != nil { - return x.CredentialKeys - } - return nil -} - -func (x *GetProviderCredentialsRequest) GetMinimumRemainingLifetime() *durationpb.Duration { - if x != nil { - return x.MinimumRemainingLifetime - } - return nil -} - -// Usable runtime value, never gateway-only minting material. -type ProviderCredentialValue struct { - state protoimpl.MessageState `protogen:"open.v1"` - Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` - // Absent for credentials with no declared expiration. - ExpirationTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderCredentialValue) Reset() { - *x = ProviderCredentialValue{} - mi := &file_openshell_proto_msgTypes[101] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderCredentialValue) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderCredentialValue) ProtoMessage() {} - -func (x *ProviderCredentialValue) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[101] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderCredentialValue.ProtoReflect.Descriptor instead. -func (*ProviderCredentialValue) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{101} -} - -func (x *ProviderCredentialValue) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -func (x *ProviderCredentialValue) GetExpirationTime() *timestamppb.Timestamp { - if x != nil { - return x.ExpirationTime - } - return nil -} - -type GetProviderCredentialsResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // All requested values or an error; no partial response. - Credentials map[string]*ProviderCredentialValue `protobuf:"bytes,1,rep,name=credentials,proto3" json:"credentials,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetProviderCredentialsResponse) Reset() { - *x = GetProviderCredentialsResponse{} - mi := &file_openshell_proto_msgTypes[102] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetProviderCredentialsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetProviderCredentialsResponse) ProtoMessage() {} - -func (x *GetProviderCredentialsResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[102] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetProviderCredentialsResponse.ProtoReflect.Descriptor instead. -func (*GetProviderCredentialsResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{102} -} - -func (x *GetProviderCredentialsResponse) GetCredentials() map[string]*ProviderCredentialValue { - if x != nil { - return x.Credentials - } - return nil -} - -// List providers request. -type ListProvidersRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Named and all-workspaces selections are accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // The maximum number of providers to return. Zero uses 100. Values above - // 1000 are coerced to 1000; negative values are invalid. - PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - // Token from a previous ListProviders response. All other request parameters - // except page_size must match the request that produced it. - PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` - // Optional label selector for filtering (format: "key1=value1,key2=value2"). - LabelSelector string `protobuf:"bytes,4,opt,name=label_selector,json=labelSelector,proto3" json:"label_selector,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListProvidersRequest) Reset() { - *x = ListProvidersRequest{} - mi := &file_openshell_proto_msgTypes[103] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListProvidersRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListProvidersRequest) ProtoMessage() {} - -func (x *ListProvidersRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[103] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListProvidersRequest.ProtoReflect.Descriptor instead. -func (*ListProvidersRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{103} -} - -func (x *ListProvidersRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ListProvidersRequest) GetPageSize() int32 { - if x != nil { - return x.PageSize - } - return 0 -} - -func (x *ListProvidersRequest) GetPageToken() string { - if x != nil { - return x.PageToken - } - return "" -} - -func (x *ListProvidersRequest) GetLabelSelector() string { - if x != nil { - return x.LabelSelector - } - return "" -} - -// Update provider request. -type UpdateProviderRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Provider *datamodelv1.Provider `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - // Optional per-credential expiry timestamps to merge into the provider. - // Omitted keys are unchanged. Use clear_credential_expiration_keys to remove - // an existing expiry. - CredentialExpirationTimes map[string]*timestamppb.Timestamp `protobuf:"bytes,102,rep,name=credential_expiration_times,json=credentialExpirationTimes,proto3" json:"credential_expiration_times,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Credential keys whose existing expiry should be removed. - ClearCredentialExpirationKeys []string `protobuf:"bytes,103,rep,name=clear_credential_expiration_keys,json=clearCredentialExpirationKeys,proto3" json:"clear_credential_expiration_keys,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,4,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UpdateProviderRequest) Reset() { - *x = UpdateProviderRequest{} - mi := &file_openshell_proto_msgTypes[104] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UpdateProviderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateProviderRequest) ProtoMessage() {} - -func (x *UpdateProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[104] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateProviderRequest.ProtoReflect.Descriptor instead. -func (*UpdateProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{104} -} - -func (x *UpdateProviderRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *UpdateProviderRequest) GetProvider() *datamodelv1.Provider { - if x != nil { - return x.Provider - } - return nil -} - -func (x *UpdateProviderRequest) GetCredentialExpirationTimes() map[string]*timestamppb.Timestamp { - if x != nil { - return x.CredentialExpirationTimes - } - return nil -} - -func (x *UpdateProviderRequest) GetClearCredentialExpirationKeys() []string { - if x != nil { - return x.ClearCredentialExpirationKeys - } - return nil -} - -func (x *UpdateProviderRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Delete provider request. -type DeleteProviderRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - AllowMissing bool `protobuf:"varint,4,opt,name=allow_missing,json=allowMissing,proto3" json:"allow_missing,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteProviderRequest) Reset() { - *x = DeleteProviderRequest{} - mi := &file_openshell_proto_msgTypes[105] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteProviderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteProviderRequest) ProtoMessage() {} - -func (x *DeleteProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[105] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteProviderRequest.ProtoReflect.Descriptor instead. -func (*DeleteProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{105} -} - -func (x *DeleteProviderRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *DeleteProviderRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *DeleteProviderRequest) GetAllowMissing() bool { - if x != nil { - return x.AllowMissing - } - return false -} - -func (x *DeleteProviderRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Provider response. -type ProviderResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Provider *datamodelv1.Provider `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - // Selection-time sandbox target set for an update, with one receipt per target. - // Sandboxes attached later are outside this operation's readiness result. - TargetReceipts []*ProviderMutationReceipt `protobuf:"bytes,2,rep,name=target_receipts,json=targetReceipts,proto3" json:"target_receipts,omitempty"` - // Identifies the update even when its target set is empty. - MutationId string `protobuf:"bytes,3,opt,name=mutation_id,json=mutationId,proto3" json:"mutation_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderResponse) Reset() { - *x = ProviderResponse{} - mi := &file_openshell_proto_msgTypes[106] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderResponse) ProtoMessage() {} - -func (x *ProviderResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[106] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderResponse.ProtoReflect.Descriptor instead. -func (*ProviderResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{106} -} - -func (x *ProviderResponse) GetProvider() *datamodelv1.Provider { - if x != nil { - return x.Provider - } - return nil -} - -func (x *ProviderResponse) GetTargetReceipts() []*ProviderMutationReceipt { - if x != nil { - return x.TargetReceipts - } - return nil -} - -func (x *ProviderResponse) GetMutationId() string { - if x != nil { - return x.MutationId - } - return "" -} - -// List providers response. -type ListProvidersResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Providers []*datamodelv1.Provider `protobuf:"bytes,1,rep,name=providers,proto3" json:"providers,omitempty"` - // Token for the next page. Empty when there are no subsequent pages. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListProvidersResponse) Reset() { - *x = ListProvidersResponse{} - mi := &file_openshell_proto_msgTypes[107] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListProvidersResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListProvidersResponse) ProtoMessage() {} - -func (x *ListProvidersResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[107] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListProvidersResponse.ProtoReflect.Descriptor instead. -func (*ListProvidersResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{107} -} - -func (x *ListProvidersResponse) GetProviders() []*datamodelv1.Provider { - if x != nil { - return x.Providers - } - return nil -} - -func (x *ListProvidersResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -// List provider type profiles request. -type ListProviderProfilesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Omit for platform profiles; otherwise select one named workspace. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // The maximum number of profiles to return. Zero uses 100. Values above - // 1000 are coerced to 1000; negative values are invalid. - PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - // Token from a previous ListProviderProfiles response. All other request - // parameters except page_size must match the request that produced it. - PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListProviderProfilesRequest) Reset() { - *x = ListProviderProfilesRequest{} - mi := &file_openshell_proto_msgTypes[108] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListProviderProfilesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListProviderProfilesRequest) ProtoMessage() {} - -func (x *ListProviderProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[108] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListProviderProfilesRequest.ProtoReflect.Descriptor instead. -func (*ListProviderProfilesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{108} -} - -func (x *ListProviderProfilesRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ListProviderProfilesRequest) GetPageSize() int32 { - if x != nil { - return x.PageSize - } - return 0 -} - -func (x *ListProviderProfilesRequest) GetPageToken() string { - if x != nil { - return x.PageToken - } - return "" -} - -// Fetch provider type profile request. -type GetProviderProfileRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Omit for platform profiles; otherwise select one named workspace. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetProviderProfileRequest) Reset() { - *x = GetProviderProfileRequest{} - mi := &file_openshell_proto_msgTypes[109] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetProviderProfileRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetProviderProfileRequest) ProtoMessage() {} - -func (x *GetProviderProfileRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[109] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetProviderProfileRequest.ProtoReflect.Descriptor instead. -func (*GetProviderProfileRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{109} -} - -func (x *GetProviderProfileRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetProviderProfileRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -// Provider profile payload with optional source metadata for diagnostics. -type ProviderProfileImportItem struct { - state protoimpl.MessageState `protogen:"open.v1"` - Profile *ProviderProfile `protobuf:"bytes,1,opt,name=profile,proto3" json:"profile,omitempty"` - Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderProfileImportItem) Reset() { - *x = ProviderProfileImportItem{} - mi := &file_openshell_proto_msgTypes[110] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderProfileImportItem) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderProfileImportItem) ProtoMessage() {} - -func (x *ProviderProfileImportItem) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[110] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderProfileImportItem.ProtoReflect.Descriptor instead. -func (*ProviderProfileImportItem) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{110} -} - -func (x *ProviderProfileImportItem) GetProfile() *ProviderProfile { - if x != nil { - return x.Profile - } - return nil -} - -func (x *ProviderProfileImportItem) GetSource() string { - if x != nil { - return x.Source - } - return "" -} - -// Provider profile validation diagnostic. -type ProviderProfileDiagnostic struct { - state protoimpl.MessageState `protogen:"open.v1"` - Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` - ProfileId string `protobuf:"bytes,2,opt,name=profile_id,json=profileId,proto3" json:"profile_id,omitempty"` - Field string `protobuf:"bytes,3,opt,name=field,proto3" json:"field,omitempty"` - Message string `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` - Severity string `protobuf:"bytes,5,opt,name=severity,proto3" json:"severity,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderProfileDiagnostic) Reset() { - *x = ProviderProfileDiagnostic{} - mi := &file_openshell_proto_msgTypes[111] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderProfileDiagnostic) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderProfileDiagnostic) ProtoMessage() {} - -func (x *ProviderProfileDiagnostic) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[111] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderProfileDiagnostic.ProtoReflect.Descriptor instead. -func (*ProviderProfileDiagnostic) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{111} -} - -func (x *ProviderProfileDiagnostic) GetSource() string { - if x != nil { - return x.Source - } - return "" -} - -func (x *ProviderProfileDiagnostic) GetProfileId() string { - if x != nil { - return x.ProfileId - } - return "" -} - -func (x *ProviderProfileDiagnostic) GetField() string { - if x != nil { - return x.Field - } - return "" -} - -func (x *ProviderProfileDiagnostic) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *ProviderProfileDiagnostic) GetSeverity() string { - if x != nil { - return x.Severity - } - return "" -} - -// Endpoint selector for token grant audience overrides. -type ProviderCredentialTokenGrantAudienceOverride struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Optional: endpoint host selector. If omitted, inherits the profile endpoint host. - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - // Optional: endpoint port selector. If omitted, matches the expanded profile endpoint port. - Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` - // Optional: endpoint path selector. If omitted, inherits the profile endpoint path. - Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` - // Resource audience to request for matching endpoints. - Audience string `protobuf:"bytes,4,opt,name=audience,proto3" json:"audience,omitempty"` - // Optional: OAuth2 scopes to request. If omitted, inherits the token grant scopes. - Scopes []string `protobuf:"bytes,5,rep,name=scopes,proto3" json:"scopes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderCredentialTokenGrantAudienceOverride) Reset() { - *x = ProviderCredentialTokenGrantAudienceOverride{} - mi := &file_openshell_proto_msgTypes[112] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderCredentialTokenGrantAudienceOverride) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderCredentialTokenGrantAudienceOverride) ProtoMessage() {} - -func (x *ProviderCredentialTokenGrantAudienceOverride) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[112] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderCredentialTokenGrantAudienceOverride.ProtoReflect.Descriptor instead. -func (*ProviderCredentialTokenGrantAudienceOverride) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{112} -} - -func (x *ProviderCredentialTokenGrantAudienceOverride) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *ProviderCredentialTokenGrantAudienceOverride) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - -func (x *ProviderCredentialTokenGrantAudienceOverride) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *ProviderCredentialTokenGrantAudienceOverride) GetAudience() string { - if x != nil { - return x.Audience - } - return "" -} - -func (x *ProviderCredentialTokenGrantAudienceOverride) GetScopes() []string { - if x != nil { - return x.Scopes - } - return nil -} - -type ProviderCredentialTokenGrantSubjectToken struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Source for the token exchange subject token. Phase one supports - // "provider_credential". - Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` - // Provider credential key that stores the subject token. - Credential string `protobuf:"bytes,2,opt,name=credential,proto3" json:"credential,omitempty"` - // OAuth2 subject_token_type. If omitted, OpenShell uses - // urn:ietf:params:oauth:token-type:access_token. - SubjectTokenType string `protobuf:"bytes,3,opt,name=subject_token_type,json=subjectTokenType,proto3" json:"subject_token_type,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderCredentialTokenGrantSubjectToken) Reset() { - *x = ProviderCredentialTokenGrantSubjectToken{} - mi := &file_openshell_proto_msgTypes[113] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderCredentialTokenGrantSubjectToken) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderCredentialTokenGrantSubjectToken) ProtoMessage() {} - -func (x *ProviderCredentialTokenGrantSubjectToken) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[113] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderCredentialTokenGrantSubjectToken.ProtoReflect.Descriptor instead. -func (*ProviderCredentialTokenGrantSubjectToken) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{113} -} - -func (x *ProviderCredentialTokenGrantSubjectToken) GetSource() string { - if x != nil { - return x.Source - } - return "" -} - -func (x *ProviderCredentialTokenGrantSubjectToken) GetCredential() string { - if x != nil { - return x.Credential - } - return "" -} - -func (x *ProviderCredentialTokenGrantSubjectToken) GetSubjectTokenType() string { - if x != nil { - return x.SubjectTokenType - } - return "" -} - -type ProviderCredentialTokenGrant struct { - state protoimpl.MessageState `protogen:"open.v1"` - // OAuth2 token endpoint URL (e.g., https://keycloak.example.com/realms/my-realm/protocol/openid-connect/token) - TokenEndpoint string `protobuf:"bytes,1,opt,name=token_endpoint,json=tokenEndpoint,proto3" json:"token_endpoint,omitempty"` - // Optional: default resource audience to request from the token service - Audience string `protobuf:"bytes,2,opt,name=audience,proto3" json:"audience,omitempty"` - // Optional: audience to request when fetching the JWT-SVID from SPIRE. - // If omitted, the sandbox derives this from token_endpoint. - JwtSvidAudience string `protobuf:"bytes,6,opt,name=jwt_svid_audience,json=jwtSvidAudience,proto3" json:"jwt_svid_audience,omitempty"` - // Optional: OAuth2 scopes to request - Scopes []string `protobuf:"bytes,3,rep,name=scopes,proto3" json:"scopes,omitempty"` - // Optional token cache TTL override. If absent, use expires_in from the token response. - CacheTtl *durationpb.Duration `protobuf:"bytes,104,opt,name=cache_ttl,json=cacheTtl,proto3" json:"cache_ttl,omitempty"` - // Optional: endpoint-specific resource audience overrides. - AudienceOverrides []*ProviderCredentialTokenGrantAudienceOverride `protobuf:"bytes,5,rep,name=audience_overrides,json=audienceOverrides,proto3" json:"audience_overrides,omitempty"` - // Optional: OAuth2 client_assertion_type value. If omitted, OpenShell uses - // urn:ietf:params:oauth:client-assertion-type:jwt-bearer. - ClientAssertionType string `protobuf:"bytes,7,opt,name=client_assertion_type,json=clientAssertionType,proto3" json:"client_assertion_type,omitempty"` - // Grant type. If omitted/unspecified, OpenShell treats this as client_credentials - // for backwards compatibility. - GrantType ProviderCredentialTokenGrantType `protobuf:"varint,8,opt,name=grant_type,json=grantType,proto3,enum=openshell.v1.ProviderCredentialTokenGrantType" json:"grant_type,omitempty"` - // Subject token metadata for token_exchange grants. - SubjectToken *ProviderCredentialTokenGrantSubjectToken `protobuf:"bytes,9,opt,name=subject_token,json=subjectToken,proto3" json:"subject_token,omitempty"` - // OAuth2 requested_token_type. If omitted for token_exchange, OpenShell uses - // urn:ietf:params:oauth:token-type:access_token. - RequestedTokenType string `protobuf:"bytes,10,opt,name=requested_token_type,json=requestedTokenType,proto3" json:"requested_token_type,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderCredentialTokenGrant) Reset() { - *x = ProviderCredentialTokenGrant{} - mi := &file_openshell_proto_msgTypes[114] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderCredentialTokenGrant) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderCredentialTokenGrant) ProtoMessage() {} - -func (x *ProviderCredentialTokenGrant) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[114] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderCredentialTokenGrant.ProtoReflect.Descriptor instead. -func (*ProviderCredentialTokenGrant) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{114} -} - -func (x *ProviderCredentialTokenGrant) GetTokenEndpoint() string { - if x != nil { - return x.TokenEndpoint - } - return "" -} - -func (x *ProviderCredentialTokenGrant) GetAudience() string { - if x != nil { - return x.Audience - } - return "" -} - -func (x *ProviderCredentialTokenGrant) GetJwtSvidAudience() string { - if x != nil { - return x.JwtSvidAudience - } - return "" -} - -func (x *ProviderCredentialTokenGrant) GetScopes() []string { - if x != nil { - return x.Scopes - } - return nil -} - -func (x *ProviderCredentialTokenGrant) GetCacheTtl() *durationpb.Duration { - if x != nil { - return x.CacheTtl - } - return nil -} - -func (x *ProviderCredentialTokenGrant) GetAudienceOverrides() []*ProviderCredentialTokenGrantAudienceOverride { - if x != nil { - return x.AudienceOverrides - } - return nil -} - -func (x *ProviderCredentialTokenGrant) GetClientAssertionType() string { - if x != nil { - return x.ClientAssertionType - } - return "" -} - -func (x *ProviderCredentialTokenGrant) GetGrantType() ProviderCredentialTokenGrantType { - if x != nil { - return x.GrantType - } - return ProviderCredentialTokenGrantType_PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_UNSPECIFIED -} - -func (x *ProviderCredentialTokenGrant) GetSubjectToken() *ProviderCredentialTokenGrantSubjectToken { - if x != nil { - return x.SubjectToken - } - return nil -} - -func (x *ProviderCredentialTokenGrant) GetRequestedTokenType() string { - if x != nil { - return x.RequestedTokenType - } - return "" -} - -// Provider credential declaration. -type ProviderProfileCredential struct { - state protoimpl.MessageState `protogen:"open.v1"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` - EnvVars []string `protobuf:"bytes,3,rep,name=env_vars,json=envVars,proto3" json:"env_vars,omitempty"` - Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"` - AuthStyle string `protobuf:"bytes,5,opt,name=auth_style,json=authStyle,proto3" json:"auth_style,omitempty"` - HeaderName string `protobuf:"bytes,6,opt,name=header_name,json=headerName,proto3" json:"header_name,omitempty"` - QueryParam string `protobuf:"bytes,7,opt,name=query_param,json=queryParam,proto3" json:"query_param,omitempty"` - Refresh *ProviderCredentialRefresh `protobuf:"bytes,8,opt,name=refresh,proto3" json:"refresh,omitempty"` - PathTemplate string `protobuf:"bytes,9,opt,name=path_template,json=pathTemplate,proto3" json:"path_template,omitempty"` - TokenGrant *ProviderCredentialTokenGrant `protobuf:"bytes,10,opt,name=token_grant,json=tokenGrant,proto3" json:"token_grant,omitempty"` - // Output-only gateway-derived endpoint authorities for resolved token grants. - // For inspected L7 requests, empty means no endpoint may obtain this grant. - // L4 injection uses selectors without checking these authorities. - // Profile-authored values are ignored; normal policies name the originating - // provider endpoint, while a global policy uses its own endpoint authorities. - TokenGrantOwners []string `protobuf:"bytes,11,rep,name=token_grant_owners,json=tokenGrantOwners,proto3" json:"token_grant_owners,omitempty"` - // Opt in to an opaque identity-bound environment placeholder for every env - // var declared by this credential. Use this only when an external credential - // updater requires one placeholder across value-only provider revisions. - // Gateway-managed refresh and token-grant credentials cannot opt in because - // they require identity-bearing handles for authorization-epoch revocation. - StablePlaceholder bool `protobuf:"varint,12,opt,name=stable_placeholder,json=stablePlaceholder,proto3" json:"stable_placeholder,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderProfileCredential) Reset() { - *x = ProviderProfileCredential{} - mi := &file_openshell_proto_msgTypes[115] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderProfileCredential) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderProfileCredential) ProtoMessage() {} - -func (x *ProviderProfileCredential) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[115] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderProfileCredential.ProtoReflect.Descriptor instead. -func (*ProviderProfileCredential) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{115} -} - -func (x *ProviderProfileCredential) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ProviderProfileCredential) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *ProviderProfileCredential) GetEnvVars() []string { - if x != nil { - return x.EnvVars - } - return nil -} - -func (x *ProviderProfileCredential) GetRequired() bool { - if x != nil { - return x.Required - } - return false -} - -func (x *ProviderProfileCredential) GetAuthStyle() string { - if x != nil { - return x.AuthStyle - } - return "" -} - -func (x *ProviderProfileCredential) GetHeaderName() string { - if x != nil { - return x.HeaderName - } - return "" -} - -func (x *ProviderProfileCredential) GetQueryParam() string { - if x != nil { - return x.QueryParam - } - return "" -} - -func (x *ProviderProfileCredential) GetRefresh() *ProviderCredentialRefresh { - if x != nil { - return x.Refresh - } - return nil -} - -func (x *ProviderProfileCredential) GetPathTemplate() string { - if x != nil { - return x.PathTemplate - } - return "" -} - -func (x *ProviderProfileCredential) GetTokenGrant() *ProviderCredentialTokenGrant { - if x != nil { - return x.TokenGrant - } - return nil -} - -func (x *ProviderProfileCredential) GetTokenGrantOwners() []string { - if x != nil { - return x.TokenGrantOwners - } - return nil -} - -func (x *ProviderProfileCredential) GetStablePlaceholder() bool { - if x != nil { - return x.StablePlaceholder - } - return false -} - -type ProviderCredentialRefreshMaterial struct { - state protoimpl.MessageState `protogen:"open.v1"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` - Required bool `protobuf:"varint,3,opt,name=required,proto3" json:"required,omitempty"` - Secret bool `protobuf:"varint,4,opt,name=secret,proto3" json:"secret,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderCredentialRefreshMaterial) Reset() { - *x = ProviderCredentialRefreshMaterial{} - mi := &file_openshell_proto_msgTypes[116] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderCredentialRefreshMaterial) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderCredentialRefreshMaterial) ProtoMessage() {} - -func (x *ProviderCredentialRefreshMaterial) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[116] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderCredentialRefreshMaterial.ProtoReflect.Descriptor instead. -func (*ProviderCredentialRefreshMaterial) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{116} -} - -func (x *ProviderCredentialRefreshMaterial) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ProviderCredentialRefreshMaterial) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *ProviderCredentialRefreshMaterial) GetRequired() bool { - if x != nil { - return x.Required - } - return false -} - -func (x *ProviderCredentialRefreshMaterial) GetSecret() bool { - if x != nil { - return x.Secret - } - return false -} - -// Declares that a single refresh operation mints more than one credential. -// The refresh is attached to a primary credential; each additional output -// maps a strategy-defined semantic output id to a sibling credential whose -// env_vars receive the minted value. -type ProviderCredentialRefreshOutput struct { - state protoimpl.MessageState `protogen:"open.v1"` - Output string `protobuf:"bytes,1,opt,name=output,proto3" json:"output,omitempty"` // strategy-defined semantic output id (e.g. "session_token") - Credential string `protobuf:"bytes,2,opt,name=credential,proto3" json:"credential,omitempty"` // sibling credential name whose env_vars receive this output - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderCredentialRefreshOutput) Reset() { - *x = ProviderCredentialRefreshOutput{} - mi := &file_openshell_proto_msgTypes[117] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderCredentialRefreshOutput) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderCredentialRefreshOutput) ProtoMessage() {} - -func (x *ProviderCredentialRefreshOutput) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[117] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderCredentialRefreshOutput.ProtoReflect.Descriptor instead. -func (*ProviderCredentialRefreshOutput) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{117} -} - -func (x *ProviderCredentialRefreshOutput) GetOutput() string { - if x != nil { - return x.Output - } - return "" -} - -func (x *ProviderCredentialRefreshOutput) GetCredential() string { - if x != nil { - return x.Credential - } - return "" -} - -type ProviderCredentialRefresh struct { - state protoimpl.MessageState `protogen:"open.v1"` - Strategy ProviderCredentialRefreshStrategy `protobuf:"varint,1,opt,name=strategy,proto3,enum=openshell.v1.ProviderCredentialRefreshStrategy" json:"strategy,omitempty"` - TokenUrl string `protobuf:"bytes,2,opt,name=token_url,json=tokenUrl,proto3" json:"token_url,omitempty"` - Scopes []string `protobuf:"bytes,3,rep,name=scopes,proto3" json:"scopes,omitempty"` - RefreshBefore *durationpb.Duration `protobuf:"bytes,104,opt,name=refresh_before,json=refreshBefore,proto3" json:"refresh_before,omitempty"` - MaxLifetime *durationpb.Duration `protobuf:"bytes,105,opt,name=max_lifetime,json=maxLifetime,proto3" json:"max_lifetime,omitempty"` - Material []*ProviderCredentialRefreshMaterial `protobuf:"bytes,6,rep,name=material,proto3" json:"material,omitempty"` - AdditionalOutputs []*ProviderCredentialRefreshOutput `protobuf:"bytes,7,rep,name=additional_outputs,json=additionalOutputs,proto3" json:"additional_outputs,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderCredentialRefresh) Reset() { - *x = ProviderCredentialRefresh{} - mi := &file_openshell_proto_msgTypes[118] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderCredentialRefresh) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderCredentialRefresh) ProtoMessage() {} - -func (x *ProviderCredentialRefresh) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[118] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderCredentialRefresh.ProtoReflect.Descriptor instead. -func (*ProviderCredentialRefresh) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{118} -} - -func (x *ProviderCredentialRefresh) GetStrategy() ProviderCredentialRefreshStrategy { - if x != nil { - return x.Strategy - } - return ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED -} - -func (x *ProviderCredentialRefresh) GetTokenUrl() string { - if x != nil { - return x.TokenUrl - } - return "" -} - -func (x *ProviderCredentialRefresh) GetScopes() []string { - if x != nil { - return x.Scopes - } - return nil -} - -func (x *ProviderCredentialRefresh) GetRefreshBefore() *durationpb.Duration { - if x != nil { - return x.RefreshBefore - } - return nil -} - -func (x *ProviderCredentialRefresh) GetMaxLifetime() *durationpb.Duration { - if x != nil { - return x.MaxLifetime - } - return nil -} - -func (x *ProviderCredentialRefresh) GetMaterial() []*ProviderCredentialRefreshMaterial { - if x != nil { - return x.Material - } - return nil -} - -func (x *ProviderCredentialRefresh) GetAdditionalOutputs() []*ProviderCredentialRefreshOutput { - if x != nil { - return x.AdditionalOutputs - } - return nil -} - -type ProviderCredentialRefreshStatus struct { - state protoimpl.MessageState `protogen:"open.v1"` - Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - ProviderId string `protobuf:"bytes,2,opt,name=provider_id,json=providerId,proto3" json:"provider_id,omitempty"` - CredentialKey string `protobuf:"bytes,3,opt,name=credential_key,json=credentialKey,proto3" json:"credential_key,omitempty"` - Strategy ProviderCredentialRefreshStrategy `protobuf:"varint,4,opt,name=strategy,proto3,enum=openshell.v1.ProviderCredentialRefreshStrategy" json:"strategy,omitempty"` - Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` - ExpirationTime *timestamppb.Timestamp `protobuf:"bytes,106,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` - // Next automatic refresh time. Absence means no automatic retry is scheduled; - // use recovery_action to determine the required recovery workflow. - NextRefreshTime *timestamppb.Timestamp `protobuf:"bytes,107,opt,name=next_refresh_time,json=nextRefreshTime,proto3" json:"next_refresh_time,omitempty"` - LastRefreshTime *timestamppb.Timestamp `protobuf:"bytes,108,opt,name=last_refresh_time,json=lastRefreshTime,proto3" json:"last_refresh_time,omitempty"` - LastError string `protobuf:"bytes,9,opt,name=last_error,json=lastError,proto3" json:"last_error,omitempty"` - RecoveryAction ProviderCredentialRefreshRecoveryAction `protobuf:"varint,10,opt,name=recovery_action,json=recoveryAction,proto3,enum=openshell.v1.ProviderCredentialRefreshRecoveryAction" json:"recovery_action,omitempty"` - // Stable gateway-owned failure identifier, for example - // "oauth_invalid_grant". This is not provider-controlled prose and - // incorporates any recognized top-level OAuth error classification. - FailureCode string `protobuf:"bytes,11,opt,name=failure_code,json=failureCode,proto3" json:"failure_code,omitempty"` - // A bounded, recognized provider subtype that refines failure_code; clients - // do not need a separate provider_error field. Unknown provider-controlled - // values are not persisted or returned. - ProviderErrorSubtype string `protobuf:"bytes,12,opt,name=provider_error_subtype,json=providerErrorSubtype,proto3" json:"provider_error_subtype,omitempty"` - LastErrorTime *timestamppb.Timestamp `protobuf:"bytes,113,opt,name=last_error_time,json=lastErrorTime,proto3" json:"last_error_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderCredentialRefreshStatus) Reset() { - *x = ProviderCredentialRefreshStatus{} - mi := &file_openshell_proto_msgTypes[119] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderCredentialRefreshStatus) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderCredentialRefreshStatus) ProtoMessage() {} - -func (x *ProviderCredentialRefreshStatus) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[119] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderCredentialRefreshStatus.ProtoReflect.Descriptor instead. -func (*ProviderCredentialRefreshStatus) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{119} -} - -func (x *ProviderCredentialRefreshStatus) GetProvider() string { - if x != nil { - return x.Provider - } - return "" -} - -func (x *ProviderCredentialRefreshStatus) GetProviderId() string { - if x != nil { - return x.ProviderId - } - return "" -} - -func (x *ProviderCredentialRefreshStatus) GetCredentialKey() string { - if x != nil { - return x.CredentialKey - } - return "" -} - -func (x *ProviderCredentialRefreshStatus) GetStrategy() ProviderCredentialRefreshStrategy { - if x != nil { - return x.Strategy - } - return ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED -} - -func (x *ProviderCredentialRefreshStatus) GetStatus() string { - if x != nil { - return x.Status - } - return "" -} - -func (x *ProviderCredentialRefreshStatus) GetExpirationTime() *timestamppb.Timestamp { - if x != nil { - return x.ExpirationTime - } - return nil -} - -func (x *ProviderCredentialRefreshStatus) GetNextRefreshTime() *timestamppb.Timestamp { - if x != nil { - return x.NextRefreshTime - } - return nil -} - -func (x *ProviderCredentialRefreshStatus) GetLastRefreshTime() *timestamppb.Timestamp { - if x != nil { - return x.LastRefreshTime - } - return nil -} - -func (x *ProviderCredentialRefreshStatus) GetLastError() string { - if x != nil { - return x.LastError - } - return "" -} - -func (x *ProviderCredentialRefreshStatus) GetRecoveryAction() ProviderCredentialRefreshRecoveryAction { - if x != nil { - return x.RecoveryAction - } - return ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED -} - -func (x *ProviderCredentialRefreshStatus) GetFailureCode() string { - if x != nil { - return x.FailureCode - } - return "" -} - -func (x *ProviderCredentialRefreshStatus) GetProviderErrorSubtype() string { - if x != nil { - return x.ProviderErrorSubtype - } - return "" -} - -func (x *ProviderCredentialRefreshStatus) GetLastErrorTime() *timestamppb.Timestamp { - if x != nil { - return x.LastErrorTime - } - return nil -} - -// Provider profile local discovery declaration. -type ProviderProfileDiscovery struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Credential names from ProviderProfile.credentials eligible for local discovery. - Credentials []string `protobuf:"bytes,1,rep,name=credentials,proto3" json:"credentials,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderProfileDiscovery) Reset() { - *x = ProviderProfileDiscovery{} - mi := &file_openshell_proto_msgTypes[120] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderProfileDiscovery) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderProfileDiscovery) ProtoMessage() {} - -func (x *ProviderProfileDiscovery) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[120] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderProfileDiscovery.ProtoReflect.Descriptor instead. -func (*ProviderProfileDiscovery) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{120} -} - -func (x *ProviderProfileDiscovery) GetCredentials() []string { - if x != nil { - return x.Credentials - } - return nil -} - -type GetProviderRefreshStatusRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - CredentialKey string `protobuf:"bytes,2,opt,name=credential_key,json=credentialKey,proto3" json:"credential_key,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetProviderRefreshStatusRequest) Reset() { - *x = GetProviderRefreshStatusRequest{} - mi := &file_openshell_proto_msgTypes[121] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetProviderRefreshStatusRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetProviderRefreshStatusRequest) ProtoMessage() {} - -func (x *GetProviderRefreshStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[121] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetProviderRefreshStatusRequest.ProtoReflect.Descriptor instead. -func (*GetProviderRefreshStatusRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{121} -} - -func (x *GetProviderRefreshStatusRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetProviderRefreshStatusRequest) GetProvider() string { - if x != nil { - return x.Provider - } - return "" -} - -func (x *GetProviderRefreshStatusRequest) GetCredentialKey() string { - if x != nil { - return x.CredentialKey - } - return "" -} - -type GetProviderRefreshStatusResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Credentials []*ProviderCredentialRefreshStatus `protobuf:"bytes,1,rep,name=credentials,proto3" json:"credentials,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetProviderRefreshStatusResponse) Reset() { - *x = GetProviderRefreshStatusResponse{} - mi := &file_openshell_proto_msgTypes[122] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetProviderRefreshStatusResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetProviderRefreshStatusResponse) ProtoMessage() {} - -func (x *GetProviderRefreshStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[122] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetProviderRefreshStatusResponse.ProtoReflect.Descriptor instead. -func (*GetProviderRefreshStatusResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{122} -} - -func (x *GetProviderRefreshStatusResponse) GetCredentials() []*ProviderCredentialRefreshStatus { - if x != nil { - return x.Credentials - } - return nil -} - -type ConfigureProviderRefreshRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,7,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - CredentialKey string `protobuf:"bytes,2,opt,name=credential_key,json=credentialKey,proto3" json:"credential_key,omitempty"` - Strategy ProviderCredentialRefreshStrategy `protobuf:"varint,3,opt,name=strategy,proto3,enum=openshell.v1.ProviderCredentialRefreshStrategy" json:"strategy,omitempty"` - Material map[string]string `protobuf:"bytes,4,rep,name=material,proto3" json:"material,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Additional material names the caller requests be stored as secrets. Every - // name must be present in material. The server also classifies secrets from - // the authoritative provider profile and refresh strategy. - SecretMaterialKeys []string `protobuf:"bytes,5,rep,name=secret_material_keys,json=secretMaterialKeys,proto3" json:"secret_material_keys,omitempty"` - ExpirationTime *timestamppb.Timestamp `protobuf:"bytes,106,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,8,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ConfigureProviderRefreshRequest) Reset() { - *x = ConfigureProviderRefreshRequest{} - mi := &file_openshell_proto_msgTypes[123] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ConfigureProviderRefreshRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConfigureProviderRefreshRequest) ProtoMessage() {} - -func (x *ConfigureProviderRefreshRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[123] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConfigureProviderRefreshRequest.ProtoReflect.Descriptor instead. -func (*ConfigureProviderRefreshRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{123} -} - -func (x *ConfigureProviderRefreshRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ConfigureProviderRefreshRequest) GetProvider() string { - if x != nil { - return x.Provider - } - return "" -} - -func (x *ConfigureProviderRefreshRequest) GetCredentialKey() string { - if x != nil { - return x.CredentialKey - } - return "" -} - -func (x *ConfigureProviderRefreshRequest) GetStrategy() ProviderCredentialRefreshStrategy { - if x != nil { - return x.Strategy - } - return ProviderCredentialRefreshStrategy_PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED -} - -func (x *ConfigureProviderRefreshRequest) GetMaterial() map[string]string { - if x != nil { - return x.Material - } - return nil -} - -func (x *ConfigureProviderRefreshRequest) GetSecretMaterialKeys() []string { - if x != nil { - return x.SecretMaterialKeys - } - return nil -} - -func (x *ConfigureProviderRefreshRequest) GetExpirationTime() *timestamppb.Timestamp { - if x != nil { - return x.ExpirationTime - } - return nil -} - -func (x *ConfigureProviderRefreshRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -type ConfigureProviderRefreshResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Status *ProviderCredentialRefreshStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ConfigureProviderRefreshResponse) Reset() { - *x = ConfigureProviderRefreshResponse{} - mi := &file_openshell_proto_msgTypes[124] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ConfigureProviderRefreshResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConfigureProviderRefreshResponse) ProtoMessage() {} - -func (x *ConfigureProviderRefreshResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[124] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConfigureProviderRefreshResponse.ProtoReflect.Descriptor instead. -func (*ConfigureProviderRefreshResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{124} -} - -func (x *ConfigureProviderRefreshResponse) GetStatus() *ProviderCredentialRefreshStatus { - if x != nil { - return x.Status - } - return nil -} - -type RotateProviderCredentialRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - CredentialKey string `protobuf:"bytes,2,opt,name=credential_key,json=credentialKey,proto3" json:"credential_key,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,4,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RotateProviderCredentialRequest) Reset() { - *x = RotateProviderCredentialRequest{} - mi := &file_openshell_proto_msgTypes[125] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RotateProviderCredentialRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RotateProviderCredentialRequest) ProtoMessage() {} - -func (x *RotateProviderCredentialRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[125] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RotateProviderCredentialRequest.ProtoReflect.Descriptor instead. -func (*RotateProviderCredentialRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{125} -} - -func (x *RotateProviderCredentialRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *RotateProviderCredentialRequest) GetProvider() string { - if x != nil { - return x.Provider - } - return "" -} - -func (x *RotateProviderCredentialRequest) GetCredentialKey() string { - if x != nil { - return x.CredentialKey - } - return "" -} - -func (x *RotateProviderCredentialRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -type RotateProviderCredentialResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Status *ProviderCredentialRefreshStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RotateProviderCredentialResponse) Reset() { - *x = RotateProviderCredentialResponse{} - mi := &file_openshell_proto_msgTypes[126] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RotateProviderCredentialResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RotateProviderCredentialResponse) ProtoMessage() {} - -func (x *RotateProviderCredentialResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[126] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RotateProviderCredentialResponse.ProtoReflect.Descriptor instead. -func (*RotateProviderCredentialResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{126} -} - -func (x *RotateProviderCredentialResponse) GetStatus() *ProviderCredentialRefreshStatus { - if x != nil { - return x.Status - } - return nil -} - -type DeleteProviderRefreshRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,4,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - CredentialKey string `protobuf:"bytes,2,opt,name=credential_key,json=credentialKey,proto3" json:"credential_key,omitempty"` - AllowMissing bool `protobuf:"varint,5,opt,name=allow_missing,json=allowMissing,proto3" json:"allow_missing,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,6,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteProviderRefreshRequest) Reset() { - *x = DeleteProviderRefreshRequest{} - mi := &file_openshell_proto_msgTypes[127] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteProviderRefreshRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteProviderRefreshRequest) ProtoMessage() {} - -func (x *DeleteProviderRefreshRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[127] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteProviderRefreshRequest.ProtoReflect.Descriptor instead. -func (*DeleteProviderRefreshRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{127} -} - -func (x *DeleteProviderRefreshRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *DeleteProviderRefreshRequest) GetProvider() string { - if x != nil { - return x.Provider - } - return "" -} - -func (x *DeleteProviderRefreshRequest) GetCredentialKey() string { - if x != nil { - return x.CredentialKey - } - return "" -} - -func (x *DeleteProviderRefreshRequest) GetAllowMissing() bool { - if x != nil { - return x.AllowMissing - } - return false -} - -func (x *DeleteProviderRefreshRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -type DeleteProviderRefreshResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Outcome DeletionOutcome `protobuf:"varint,2,opt,name=outcome,proto3,enum=openshell.v1.DeletionOutcome" json:"outcome,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteProviderRefreshResponse) Reset() { - *x = DeleteProviderRefreshResponse{} - mi := &file_openshell_proto_msgTypes[128] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteProviderRefreshResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteProviderRefreshResponse) ProtoMessage() {} - -func (x *DeleteProviderRefreshResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[128] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteProviderRefreshResponse.ProtoReflect.Descriptor instead. -func (*DeleteProviderRefreshResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{128} -} - -func (x *DeleteProviderRefreshResponse) GetOutcome() DeletionOutcome { - if x != nil { - return x.Outcome - } - return DeletionOutcome_DELETION_OUTCOME_UNSPECIFIED -} - -// Provider type profile metadata exposed to clients. -type ProviderProfile struct { - state protoimpl.MessageState `protogen:"open.v1"` - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - DisplayName string `protobuf:"bytes,2,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - Category ProviderProfileCategory `protobuf:"varint,4,opt,name=category,proto3,enum=openshell.v1.ProviderProfileCategory" json:"category,omitempty"` - Credentials []*ProviderProfileCredential `protobuf:"bytes,5,rep,name=credentials,proto3" json:"credentials,omitempty"` - Endpoints []*sandboxv1.NetworkEndpoint `protobuf:"bytes,6,rep,name=endpoints,proto3" json:"endpoints,omitempty"` - Binaries []*sandboxv1.NetworkBinary `protobuf:"bytes,7,rep,name=binaries,proto3" json:"binaries,omitempty"` - InferenceCapable bool `protobuf:"varint,8,opt,name=inference_capable,json=inferenceCapable,proto3" json:"inference_capable,omitempty"` - Discovery *ProviderProfileDiscovery `protobuf:"bytes,9,opt,name=discovery,proto3" json:"discovery,omitempty"` - // Storage resource version for custom profiles. Built-in profiles and new - // profile files use 0. Gateway responses set this for stored custom profiles. - // Update calls use this for optimistic concurrency. - ResourceVersion uint64 `protobuf:"varint,10,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` - // Optional non-secret annotations attached by profile sources or importers. - Annotations map[string]string `protobuf:"bytes,11,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Server-set provenance: "builtin", "user", or "interceptor/{name}". - // Ignored on import/update payloads. - Source string `protobuf:"bytes,12,opt,name=source,proto3" json:"source,omitempty"` - // Server-set visibility: "platform", "workspace", or empty for - // non-scoped sources. Ignored on import/update payloads. - Scope string `protobuf:"bytes,13,opt,name=scope,proto3" json:"scope,omitempty"` - // EXPERIMENTAL: Non-secret files rendered from provider configuration for the - // workload. This API and its behavior may change or be removed. - Files []*ProviderProfileFile `protobuf:"bytes,14,rep,name=files,proto3" json:"files,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderProfile) Reset() { - *x = ProviderProfile{} - mi := &file_openshell_proto_msgTypes[129] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderProfile) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderProfile) ProtoMessage() {} - -func (x *ProviderProfile) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[129] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderProfile.ProtoReflect.Descriptor instead. -func (*ProviderProfile) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{129} -} - -func (x *ProviderProfile) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *ProviderProfile) GetDisplayName() string { - if x != nil { - return x.DisplayName - } - return "" -} - -func (x *ProviderProfile) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *ProviderProfile) GetCategory() ProviderProfileCategory { - if x != nil { - return x.Category - } - return ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_UNSPECIFIED -} - -func (x *ProviderProfile) GetCredentials() []*ProviderProfileCredential { - if x != nil { - return x.Credentials - } - return nil -} - -func (x *ProviderProfile) GetEndpoints() []*sandboxv1.NetworkEndpoint { - if x != nil { - return x.Endpoints - } - return nil -} - -func (x *ProviderProfile) GetBinaries() []*sandboxv1.NetworkBinary { - if x != nil { - return x.Binaries - } - return nil -} - -func (x *ProviderProfile) GetInferenceCapable() bool { - if x != nil { - return x.InferenceCapable - } - return false -} - -func (x *ProviderProfile) GetDiscovery() *ProviderProfileDiscovery { - if x != nil { - return x.Discovery - } - return nil -} - -func (x *ProviderProfile) GetResourceVersion() uint64 { - if x != nil { - return x.ResourceVersion - } - return 0 -} - -func (x *ProviderProfile) GetAnnotations() map[string]string { - if x != nil { - return x.Annotations - } - return nil -} - -func (x *ProviderProfile) GetSource() string { - if x != nil { - return x.Source - } - return "" -} - -func (x *ProviderProfile) GetScope() string { - if x != nil { - return x.Scope - } - return "" -} - -func (x *ProviderProfile) GetFiles() []*ProviderProfileFile { - if x != nil { - return x.Files - } - return nil -} - -// EXPERIMENTAL: Provider file templates may change or be removed. -type ProviderProfileFile struct { - state protoimpl.MessageState `protogen:"open.v1"` - // One virtual file name below /run/openshell/providers//. - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - // UTF-8 template. Only {{config.KEY}} references are supported. - Content string `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` - // Optional environment variable containing the virtual absolute path. - EnvVar string `protobuf:"bytes,3,opt,name=env_var,json=envVar,proto3" json:"env_var,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderProfileFile) Reset() { - *x = ProviderProfileFile{} - mi := &file_openshell_proto_msgTypes[130] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderProfileFile) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderProfileFile) ProtoMessage() {} - -func (x *ProviderProfileFile) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[130] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderProfileFile.ProtoReflect.Descriptor instead. -func (*ProviderProfileFile) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{130} -} - -func (x *ProviderProfileFile) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *ProviderProfileFile) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -func (x *ProviderProfileFile) GetEnvVar() string { - if x != nil { - return x.EnvVar - } - return "" -} - -// Provider profile response. -type ProviderProfileResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Profile *ProviderProfile `protobuf:"bytes,1,opt,name=profile,proto3" json:"profile,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderProfileResponse) Reset() { - *x = ProviderProfileResponse{} - mi := &file_openshell_proto_msgTypes[131] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderProfileResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderProfileResponse) ProtoMessage() {} - -func (x *ProviderProfileResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[131] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderProfileResponse.ProtoReflect.Descriptor instead. -func (*ProviderProfileResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{131} -} - -func (x *ProviderProfileResponse) GetProfile() *ProviderProfile { - if x != nil { - return x.Profile - } - return nil -} - -// List provider profiles response. -type ListProviderProfilesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Profiles []*ProviderProfile `protobuf:"bytes,1,rep,name=profiles,proto3" json:"profiles,omitempty"` - // Token for the next page. Empty when there are no subsequent pages. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListProviderProfilesResponse) Reset() { - *x = ListProviderProfilesResponse{} - mi := &file_openshell_proto_msgTypes[132] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListProviderProfilesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListProviderProfilesResponse) ProtoMessage() {} - -func (x *ListProviderProfilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[132] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListProviderProfilesResponse.ProtoReflect.Descriptor instead. -func (*ListProviderProfilesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{132} -} - -func (x *ListProviderProfilesResponse) GetProfiles() []*ProviderProfile { - if x != nil { - return x.Profiles - } - return nil -} - -func (x *ListProviderProfilesResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -// Import custom provider profiles request. -type ImportProviderProfilesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Omit for platform profiles; otherwise select one named workspace. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Profiles []*ProviderProfileImportItem `protobuf:"bytes,1,rep,name=profiles,proto3" json:"profiles,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ImportProviderProfilesRequest) Reset() { - *x = ImportProviderProfilesRequest{} - mi := &file_openshell_proto_msgTypes[133] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ImportProviderProfilesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ImportProviderProfilesRequest) ProtoMessage() {} - -func (x *ImportProviderProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[133] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ImportProviderProfilesRequest.ProtoReflect.Descriptor instead. -func (*ImportProviderProfilesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{133} -} - -func (x *ImportProviderProfilesRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ImportProviderProfilesRequest) GetProfiles() []*ProviderProfileImportItem { - if x != nil { - return x.Profiles - } - return nil -} - -func (x *ImportProviderProfilesRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Import custom provider profiles response. -type ImportProviderProfilesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Diagnostics []*ProviderProfileDiagnostic `protobuf:"bytes,1,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - Profiles []*ProviderProfile `protobuf:"bytes,2,rep,name=profiles,proto3" json:"profiles,omitempty"` - Imported bool `protobuf:"varint,3,opt,name=imported,proto3" json:"imported,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ImportProviderProfilesResponse) Reset() { - *x = ImportProviderProfilesResponse{} - mi := &file_openshell_proto_msgTypes[134] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ImportProviderProfilesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ImportProviderProfilesResponse) ProtoMessage() {} - -func (x *ImportProviderProfilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[134] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ImportProviderProfilesResponse.ProtoReflect.Descriptor instead. -func (*ImportProviderProfilesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{134} -} - -func (x *ImportProviderProfilesResponse) GetDiagnostics() []*ProviderProfileDiagnostic { - if x != nil { - return x.Diagnostics - } - return nil -} - -func (x *ImportProviderProfilesResponse) GetProfiles() []*ProviderProfile { - if x != nil { - return x.Profiles - } - return nil -} - -func (x *ImportProviderProfilesResponse) GetImported() bool { - if x != nil { - return x.Imported - } - return false -} - -// Update one custom provider profile request. -type UpdateProviderProfilesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Omit for platform profiles; otherwise select one named workspace. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,4,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Profile *ProviderProfileImportItem `protobuf:"bytes,1,opt,name=profile,proto3" json:"profile,omitempty"` - // Expected storage resource version for optimistic concurrency control. - // If 0, the server uses the resource_version embedded in profile.profile. - // Updates without a non-zero version are rejected to prevent stale files from - // silently overwriting newer profile definitions. - ExpectedResourceVersion uint64 `protobuf:"varint,2,opt,name=expected_resource_version,json=expectedResourceVersion,proto3" json:"expected_resource_version,omitempty"` - // Existing custom provider profile ID to update. The payload ID must match. - Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UpdateProviderProfilesRequest) Reset() { - *x = UpdateProviderProfilesRequest{} - mi := &file_openshell_proto_msgTypes[135] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UpdateProviderProfilesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateProviderProfilesRequest) ProtoMessage() {} - -func (x *UpdateProviderProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[135] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateProviderProfilesRequest.ProtoReflect.Descriptor instead. -func (*UpdateProviderProfilesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{135} -} - -func (x *UpdateProviderProfilesRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *UpdateProviderProfilesRequest) GetProfile() *ProviderProfileImportItem { - if x != nil { - return x.Profile - } - return nil -} - -func (x *UpdateProviderProfilesRequest) GetExpectedResourceVersion() uint64 { - if x != nil { - return x.ExpectedResourceVersion - } - return 0 -} - -func (x *UpdateProviderProfilesRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *UpdateProviderProfilesRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Update one custom provider profile response. -type UpdateProviderProfilesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Diagnostics []*ProviderProfileDiagnostic `protobuf:"bytes,1,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - Profile *ProviderProfile `protobuf:"bytes,2,opt,name=profile,proto3" json:"profile,omitempty"` - Updated bool `protobuf:"varint,3,opt,name=updated,proto3" json:"updated,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UpdateProviderProfilesResponse) Reset() { - *x = UpdateProviderProfilesResponse{} - mi := &file_openshell_proto_msgTypes[136] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UpdateProviderProfilesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateProviderProfilesResponse) ProtoMessage() {} - -func (x *UpdateProviderProfilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[136] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateProviderProfilesResponse.ProtoReflect.Descriptor instead. -func (*UpdateProviderProfilesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{136} -} - -func (x *UpdateProviderProfilesResponse) GetDiagnostics() []*ProviderProfileDiagnostic { - if x != nil { - return x.Diagnostics - } - return nil -} - -func (x *UpdateProviderProfilesResponse) GetProfile() *ProviderProfile { - if x != nil { - return x.Profile - } - return nil -} - -func (x *UpdateProviderProfilesResponse) GetUpdated() bool { - if x != nil { - return x.Updated - } - return false -} - -// Lint provider profiles request. -type LintProviderProfilesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Omit for platform profiles; otherwise select one named workspace. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Profiles []*ProviderProfileImportItem `protobuf:"bytes,1,rep,name=profiles,proto3" json:"profiles,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *LintProviderProfilesRequest) Reset() { - *x = LintProviderProfilesRequest{} - mi := &file_openshell_proto_msgTypes[137] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LintProviderProfilesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LintProviderProfilesRequest) ProtoMessage() {} - -func (x *LintProviderProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[137] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LintProviderProfilesRequest.ProtoReflect.Descriptor instead. -func (*LintProviderProfilesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{137} -} - -func (x *LintProviderProfilesRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *LintProviderProfilesRequest) GetProfiles() []*ProviderProfileImportItem { - if x != nil { - return x.Profiles - } - return nil -} - -// Lint provider profiles response. -type LintProviderProfilesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Diagnostics []*ProviderProfileDiagnostic `protobuf:"bytes,1,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - Valid bool `protobuf:"varint,2,opt,name=valid,proto3" json:"valid,omitempty"` - // Clients enabling stable_placeholder check this before import or update, - // since older gateways silently discard unknown credential fields. - SupportsStablePlaceholder bool `protobuf:"varint,3,opt,name=supports_stable_placeholder,json=supportsStablePlaceholder,proto3" json:"supports_stable_placeholder,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *LintProviderProfilesResponse) Reset() { - *x = LintProviderProfilesResponse{} - mi := &file_openshell_proto_msgTypes[138] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LintProviderProfilesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LintProviderProfilesResponse) ProtoMessage() {} - -func (x *LintProviderProfilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[138] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LintProviderProfilesResponse.ProtoReflect.Descriptor instead. -func (*LintProviderProfilesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{138} -} - -func (x *LintProviderProfilesResponse) GetDiagnostics() []*ProviderProfileDiagnostic { - if x != nil { - return x.Diagnostics - } - return nil -} - -func (x *LintProviderProfilesResponse) GetValid() bool { - if x != nil { - return x.Valid - } - return false -} - -func (x *LintProviderProfilesResponse) GetSupportsStablePlaceholder() bool { - if x != nil { - return x.SupportsStablePlaceholder - } - return false -} - -// Delete provider response. -type DeleteProviderResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Outcome DeletionOutcome `protobuf:"varint,2,opt,name=outcome,proto3,enum=openshell.v1.DeletionOutcome" json:"outcome,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteProviderResponse) Reset() { - *x = DeleteProviderResponse{} - mi := &file_openshell_proto_msgTypes[139] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteProviderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteProviderResponse) ProtoMessage() {} - -func (x *DeleteProviderResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[139] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteProviderResponse.ProtoReflect.Descriptor instead. -func (*DeleteProviderResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{139} -} - -func (x *DeleteProviderResponse) GetOutcome() DeletionOutcome { - if x != nil { - return x.Outcome - } - return DeletionOutcome_DELETION_OUTCOME_UNSPECIFIED -} - -// Delete custom provider profile request. -type DeleteProviderProfileRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Omit for platform profiles; otherwise select one named workspace. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - AllowMissing bool `protobuf:"varint,3,opt,name=allow_missing,json=allowMissing,proto3" json:"allow_missing,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,4,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteProviderProfileRequest) Reset() { - *x = DeleteProviderProfileRequest{} - mi := &file_openshell_proto_msgTypes[140] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteProviderProfileRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteProviderProfileRequest) ProtoMessage() {} - -func (x *DeleteProviderProfileRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[140] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteProviderProfileRequest.ProtoReflect.Descriptor instead. -func (*DeleteProviderProfileRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{140} -} - -func (x *DeleteProviderProfileRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *DeleteProviderProfileRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *DeleteProviderProfileRequest) GetAllowMissing() bool { - if x != nil { - return x.AllowMissing - } - return false -} - -func (x *DeleteProviderProfileRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Delete custom provider profile response. -type DeleteProviderProfileResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Outcome DeletionOutcome `protobuf:"varint,2,opt,name=outcome,proto3,enum=openshell.v1.DeletionOutcome" json:"outcome,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteProviderProfileResponse) Reset() { - *x = DeleteProviderProfileResponse{} - mi := &file_openshell_proto_msgTypes[141] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteProviderProfileResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteProviderProfileResponse) ProtoMessage() {} - -func (x *DeleteProviderProfileResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[141] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteProviderProfileResponse.ProtoReflect.Descriptor instead. -func (*DeleteProviderProfileResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{141} -} - -func (x *DeleteProviderProfileResponse) GetOutcome() DeletionOutcome { - if x != nil { - return x.Outcome - } - return DeletionOutcome_DELETION_OUTCOME_UNSPECIFIED -} - -// Get sandbox provider environment request. -type GetSandboxProviderEnvironmentRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The sandbox ID. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Whether the requesting supervisor enforces endpoint bindings for static - // provider credentials. Gateways withhold static credential material when - // this capability is absent. - SupportsStaticCredentialBindings bool `protobuf:"varint,2,opt,name=supports_static_credential_bindings,json=supportsStaticCredentialBindings,proto3" json:"supports_static_credential_bindings,omitempty"` - // Whether the requesting supervisor understands stable external-placeholder - // metadata. Gateways withhold credentials that require this behavior when - // the capability is absent. - SupportsStablePlaceholderEnvironmentKeys bool `protobuf:"varint,3,opt,name=supports_stable_placeholder_environment_keys,json=supportsStablePlaceholderEnvironmentKeys,proto3" json:"supports_stable_placeholder_environment_keys,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxProviderEnvironmentRequest) Reset() { - *x = GetSandboxProviderEnvironmentRequest{} - mi := &file_openshell_proto_msgTypes[142] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxProviderEnvironmentRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxProviderEnvironmentRequest) ProtoMessage() {} - -func (x *GetSandboxProviderEnvironmentRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[142] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxProviderEnvironmentRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxProviderEnvironmentRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{142} -} - -func (x *GetSandboxProviderEnvironmentRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *GetSandboxProviderEnvironmentRequest) GetSupportsStaticCredentialBindings() bool { - if x != nil { - return x.SupportsStaticCredentialBindings - } - return false -} - -func (x *GetSandboxProviderEnvironmentRequest) GetSupportsStablePlaceholderEnvironmentKeys() bool { - if x != nil { - return x.SupportsStablePlaceholderEnvironmentKeys - } - return false -} - -// One network endpoint at which a static provider credential may be resolved. -type StaticCredentialEndpointBinding struct { - state protoimpl.MessageState `protogen:"open.v1"` - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` - Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StaticCredentialEndpointBinding) Reset() { - *x = StaticCredentialEndpointBinding{} - mi := &file_openshell_proto_msgTypes[143] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StaticCredentialEndpointBinding) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StaticCredentialEndpointBinding) ProtoMessage() {} - -func (x *StaticCredentialEndpointBinding) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[143] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StaticCredentialEndpointBinding.ProtoReflect.Descriptor instead. -func (*StaticCredentialEndpointBinding) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{143} -} - -func (x *StaticCredentialEndpointBinding) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *StaticCredentialEndpointBinding) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - -func (x *StaticCredentialEndpointBinding) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -// Endpoint allowlist for one static provider credential environment variable. -type StaticCredentialBinding struct { - state protoimpl.MessageState `protogen:"open.v1"` - Endpoints []*StaticCredentialEndpointBinding `protobuf:"bytes,1,rep,name=endpoints,proto3" json:"endpoints,omitempty"` - // Stable identity of the provider credential that produced this binding. - // Supervisors use it to retain old revision placeholders only across - // rotations of the same provider credential. - CredentialIdentity string `protobuf:"bytes,2,opt,name=credential_identity,json=credentialIdentity,proto3" json:"credential_identity,omitempty"` - // Opaque gateway-issued handle for a stable credential lifecycle. When - // non-empty, supervisors keep the workload placeholder stable across value - // updates and replace only the resolver value. The handle changes when the - // sandbox, provider, credential key, endpoint authorization, or an applicable - // refresh authorization epoch changes. - WorkloadCredentialHandle string `protobuf:"bytes,3,opt,name=workload_credential_handle,json=workloadCredentialHandle,proto3" json:"workload_credential_handle,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StaticCredentialBinding) Reset() { - *x = StaticCredentialBinding{} - mi := &file_openshell_proto_msgTypes[144] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StaticCredentialBinding) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StaticCredentialBinding) ProtoMessage() {} - -func (x *StaticCredentialBinding) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[144] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StaticCredentialBinding.ProtoReflect.Descriptor instead. -func (*StaticCredentialBinding) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{144} -} - -func (x *StaticCredentialBinding) GetEndpoints() []*StaticCredentialEndpointBinding { - if x != nil { - return x.Endpoints - } - return nil -} - -func (x *StaticCredentialBinding) GetCredentialIdentity() string { - if x != nil { - return x.CredentialIdentity - } - return "" -} - -func (x *StaticCredentialBinding) GetWorkloadCredentialHandle() string { - if x != nil { - return x.WorkloadCredentialHandle - } - return "" -} - -// Polling projection of ProviderEnvironmentSnapshot. The parallel maps below -// are keyed by the same environment variable names. Removed with the polling -// RPCs; new provider environment fields belong on the snapshot first. -type GetSandboxProviderEnvironmentResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Provider credential environment variables. - Environment map[string]string `protobuf:"bytes,1,rep,name=environment,proto3" json:"environment,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Fingerprint for the provider credential inputs that produced environment. - ProviderEnvRevision uint64 `protobuf:"varint,2,opt,name=provider_env_revision,json=providerEnvRevision,proto3" json:"provider_env_revision,omitempty"` - // Expiration timestamps for returned environment variables. - CredentialExpirationTimes map[string]*timestamppb.Timestamp `protobuf:"bytes,103,rep,name=credential_expiration_times,json=credentialExpirationTimes,proto3" json:"credential_expiration_times,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Dynamic credentials that require token grants or other runtime injection. - // Maps endpoint-bound provider metadata to credential metadata. - // Supervisor uses this to inject Authorization headers for token grant credentials. - DynamicCredentials map[string]*ProviderProfileCredential `protobuf:"bytes,4,rep,name=dynamic_credentials,json=dynamicCredentials,proto3" json:"dynamic_credentials,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Endpoint allowlists for static credential environment variables. Metadata - // can be incomplete when a provider profile is invalid; capable supervisors - // reject all static material in that snapshot while preserving independently - // endpoint-bound dynamic credentials. - StaticCredentialBindings map[string]*StaticCredentialBinding `protobuf:"bytes,5,rep,name=static_credential_bindings,json=staticCredentialBindings,proto3" json:"static_credential_bindings,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Environment variables that contain provider configuration rather than - // credentials and therefore do not require endpoint-scoped resolution. - NonSecretEnvironmentKeys []string `protobuf:"bytes,6,rep,name=non_secret_environment_keys,json=nonSecretEnvironmentKeys,proto3" json:"non_secret_environment_keys,omitempty"` - // Attachment identity captured with the returned provider records. - ProviderAttachmentEpoch string `protobuf:"bytes,7,opt,name=provider_attachment_epoch,json=providerAttachmentEpoch,proto3" json:"provider_attachment_epoch,omitempty"` - // Effective policy identity used to derive this snapshot's endpoint bindings. - PolicyHash string `protobuf:"bytes,8,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - // Nonzero when material was withheld; installing an empty map is not readiness. - ReadinessReason ProviderReadinessReason `protobuf:"varint,9,opt,name=readiness_reason,json=readinessReason,proto3,enum=openshell.v1.ProviderReadinessReason" json:"readiness_reason,omitempty"` - // Complete desired set of non-secret managed files, keyed by absolute path. - Files map[string]string `protobuf:"bytes,10,rep,name=files,proto3" json:"files,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Credential environment variables whose opaque resolver handle remains - // stable across value-only provider revisions. These keys are still secrets; - // this metadata only selects the stable external-placeholder behavior. - StablePlaceholderEnvironmentKeys []string `protobuf:"bytes,11,rep,name=stable_placeholder_environment_keys,json=stablePlaceholderEnvironmentKeys,proto3" json:"stable_placeholder_environment_keys,omitempty"` - // Supporting gateways always acknowledge the stable-placeholder delivery - // contract, including responses without opted-in credentials. Supervisors - // require acknowledgment for declared or previously activated external - // stable credentials; ordinary legacy delivery does not require it. - SupportsStablePlaceholderEnvironmentKeys bool `protobuf:"varint,12,opt,name=supports_stable_placeholder_environment_keys,json=supportsStablePlaceholderEnvironmentKeys,proto3" json:"supports_stable_placeholder_environment_keys,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxProviderEnvironmentResponse) Reset() { - *x = GetSandboxProviderEnvironmentResponse{} - mi := &file_openshell_proto_msgTypes[145] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxProviderEnvironmentResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxProviderEnvironmentResponse) ProtoMessage() {} - -func (x *GetSandboxProviderEnvironmentResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[145] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxProviderEnvironmentResponse.ProtoReflect.Descriptor instead. -func (*GetSandboxProviderEnvironmentResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{145} -} - -func (x *GetSandboxProviderEnvironmentResponse) GetEnvironment() map[string]string { - if x != nil { - return x.Environment - } - return nil -} - -func (x *GetSandboxProviderEnvironmentResponse) GetProviderEnvRevision() uint64 { - if x != nil { - return x.ProviderEnvRevision - } - return 0 -} - -func (x *GetSandboxProviderEnvironmentResponse) GetCredentialExpirationTimes() map[string]*timestamppb.Timestamp { - if x != nil { - return x.CredentialExpirationTimes - } - return nil -} - -func (x *GetSandboxProviderEnvironmentResponse) GetDynamicCredentials() map[string]*ProviderProfileCredential { - if x != nil { - return x.DynamicCredentials - } - return nil -} - -func (x *GetSandboxProviderEnvironmentResponse) GetStaticCredentialBindings() map[string]*StaticCredentialBinding { - if x != nil { - return x.StaticCredentialBindings - } - return nil -} - -func (x *GetSandboxProviderEnvironmentResponse) GetNonSecretEnvironmentKeys() []string { - if x != nil { - return x.NonSecretEnvironmentKeys - } - return nil -} - -func (x *GetSandboxProviderEnvironmentResponse) GetProviderAttachmentEpoch() string { - if x != nil { - return x.ProviderAttachmentEpoch - } - return "" -} - -func (x *GetSandboxProviderEnvironmentResponse) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *GetSandboxProviderEnvironmentResponse) GetReadinessReason() ProviderReadinessReason { - if x != nil { - return x.ReadinessReason - } - return ProviderReadinessReason_PROVIDER_READINESS_REASON_UNSPECIFIED -} - -func (x *GetSandboxProviderEnvironmentResponse) GetFiles() map[string]string { - if x != nil { - return x.Files - } - return nil -} - -func (x *GetSandboxProviderEnvironmentResponse) GetStablePlaceholderEnvironmentKeys() []string { - if x != nil { - return x.StablePlaceholderEnvironmentKeys - } - return nil -} - -func (x *GetSandboxProviderEnvironmentResponse) GetSupportsStablePlaceholderEnvironmentKeys() bool { - if x != nil { - return x.SupportsStablePlaceholderEnvironmentKeys - } - return false -} - -// One environment value and all metadata that shares its key. Names are unique -// within a snapshot. static_credential_binding is present exactly when -// classification is STATIC_CREDENTIAL. Snapshots that violate either rule are -// invalid. -type ProviderEnvironmentValue struct { - state protoimpl.MessageState `protogen:"open.v1"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` - Classification ProviderEnvironmentValueClassification `protobuf:"varint,4,opt,name=classification,proto3,enum=openshell.v1.ProviderEnvironmentValueClassification" json:"classification,omitempty"` - StaticCredentialBinding *StaticCredentialBinding `protobuf:"bytes,5,opt,name=static_credential_binding,json=staticCredentialBinding,proto3" json:"static_credential_binding,omitempty"` - // Absolute expiration time. Absence means no expiry. - ExpirationTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` - // The static credential keeps one opaque resolver handle across value-only - // provider revisions. Set only for STATIC_CREDENTIAL values whose binding - // carries a workload_credential_handle. - StablePlaceholder bool `protobuf:"varint,6,opt,name=stable_placeholder,json=stablePlaceholder,proto3" json:"stable_placeholder,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderEnvironmentValue) Reset() { - *x = ProviderEnvironmentValue{} - mi := &file_openshell_proto_msgTypes[146] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderEnvironmentValue) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderEnvironmentValue) ProtoMessage() {} - -func (x *ProviderEnvironmentValue) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[146] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderEnvironmentValue.ProtoReflect.Descriptor instead. -func (*ProviderEnvironmentValue) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{146} -} - -func (x *ProviderEnvironmentValue) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ProviderEnvironmentValue) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -func (x *ProviderEnvironmentValue) GetClassification() ProviderEnvironmentValueClassification { - if x != nil { - return x.Classification - } - return ProviderEnvironmentValueClassification_PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_UNSPECIFIED -} - -func (x *ProviderEnvironmentValue) GetStaticCredentialBinding() *StaticCredentialBinding { - if x != nil { - return x.StaticCredentialBinding - } - return nil -} - -func (x *ProviderEnvironmentValue) GetExpirationTime() *timestamppb.Timestamp { - if x != nil { - return x.ExpirationTime - } - return nil -} - -func (x *ProviderEnvironmentValue) GetStablePlaceholder() bool { - if x != nil { - return x.StablePlaceholder - } - return false -} - -// Complete provider environment state delivered to a supervisor. This is the -// canonical provider environment shape; GetSandboxProviderEnvironmentResponse -// is its polling projection and is removed with the polling RPCs. Dynamic -// credentials are endpoint selectors rather than environment values and stay -// in their own collection. -type ProviderEnvironmentSnapshot struct { - state protoimpl.MessageState `protogen:"open.v1"` - ProviderEnvRevision uint64 `protobuf:"varint,1,opt,name=provider_env_revision,json=providerEnvRevision,proto3" json:"provider_env_revision,omitempty"` - Values []*ProviderEnvironmentValue `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"` - DynamicCredentials map[string]*ProviderProfileCredential `protobuf:"bytes,3,rep,name=dynamic_credentials,json=dynamicCredentials,proto3" json:"dynamic_credentials,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - ProviderAttachmentEpoch string `protobuf:"bytes,4,opt,name=provider_attachment_epoch,json=providerAttachmentEpoch,proto3" json:"provider_attachment_epoch,omitempty"` - PolicyHash string `protobuf:"bytes,5,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - ReadinessReason ProviderReadinessReason `protobuf:"varint,6,opt,name=readiness_reason,json=readinessReason,proto3,enum=openshell.v1.ProviderReadinessReason" json:"readiness_reason,omitempty"` - // Complete desired set of non-secret managed files, keyed by absolute path. - Files map[string]string `protobuf:"bytes,7,rep,name=files,proto3" json:"files,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderEnvironmentSnapshot) Reset() { - *x = ProviderEnvironmentSnapshot{} - mi := &file_openshell_proto_msgTypes[147] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderEnvironmentSnapshot) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderEnvironmentSnapshot) ProtoMessage() {} - -func (x *ProviderEnvironmentSnapshot) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[147] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderEnvironmentSnapshot.ProtoReflect.Descriptor instead. -func (*ProviderEnvironmentSnapshot) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{147} -} - -func (x *ProviderEnvironmentSnapshot) GetProviderEnvRevision() uint64 { - if x != nil { - return x.ProviderEnvRevision - } - return 0 -} - -func (x *ProviderEnvironmentSnapshot) GetValues() []*ProviderEnvironmentValue { - if x != nil { - return x.Values - } - return nil -} - -func (x *ProviderEnvironmentSnapshot) GetDynamicCredentials() map[string]*ProviderProfileCredential { - if x != nil { - return x.DynamicCredentials - } - return nil -} - -func (x *ProviderEnvironmentSnapshot) GetProviderAttachmentEpoch() string { - if x != nil { - return x.ProviderAttachmentEpoch - } - return "" -} - -func (x *ProviderEnvironmentSnapshot) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *ProviderEnvironmentSnapshot) GetReadinessReason() ProviderReadinessReason { - if x != nil { - return x.ReadinessReason - } - return ProviderReadinessReason_PROVIDER_READINESS_REASON_UNSPECIFIED -} - -func (x *ProviderEnvironmentSnapshot) GetFiles() map[string]string { - if x != nil { - return x.Files - } - return nil -} - -type ExchangeProviderSubjectTokenRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The sandbox ID. Must match the authenticated sandbox principal. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Attached provider record holding the configured subject token credential. - Provider string `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider,omitempty"` - // Provider profile credential that declares the token_exchange grant. - CredentialKey string `protobuf:"bytes,3,opt,name=credential_key,json=credentialKey,proto3" json:"credential_key,omitempty"` - // Supervisor JWT-SVID. The gateway verifies this and uses its `sub` claim - // as the requested audience for the intermediate token. - SupervisorJwtSvid string `protobuf:"bytes,4,opt,name=supervisor_jwt_svid,json=supervisorJwtSvid,proto3" json:"supervisor_jwt_svid,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExchangeProviderSubjectTokenRequest) Reset() { - *x = ExchangeProviderSubjectTokenRequest{} - mi := &file_openshell_proto_msgTypes[148] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExchangeProviderSubjectTokenRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExchangeProviderSubjectTokenRequest) ProtoMessage() {} - -func (x *ExchangeProviderSubjectTokenRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[148] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExchangeProviderSubjectTokenRequest.ProtoReflect.Descriptor instead. -func (*ExchangeProviderSubjectTokenRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{148} -} - -func (x *ExchangeProviderSubjectTokenRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *ExchangeProviderSubjectTokenRequest) GetProvider() string { - if x != nil { - return x.Provider - } - return "" -} - -func (x *ExchangeProviderSubjectTokenRequest) GetCredentialKey() string { - if x != nil { - return x.CredentialKey - } - return "" -} - -func (x *ExchangeProviderSubjectTokenRequest) GetSupervisorJwtSvid() string { - if x != nil { - return x.SupervisorJwtSvid - } - return "" -} - -type ExchangeProviderSubjectTokenResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` - ExpiresAfter *durationpb.Duration `protobuf:"bytes,102,opt,name=expires_after,json=expiresAfter,proto3" json:"expires_after,omitempty"` - TokenType string `protobuf:"bytes,3,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExchangeProviderSubjectTokenResponse) Reset() { - *x = ExchangeProviderSubjectTokenResponse{} - mi := &file_openshell_proto_msgTypes[149] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExchangeProviderSubjectTokenResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExchangeProviderSubjectTokenResponse) ProtoMessage() {} - -func (x *ExchangeProviderSubjectTokenResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[149] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExchangeProviderSubjectTokenResponse.ProtoReflect.Descriptor instead. -func (*ExchangeProviderSubjectTokenResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{149} -} - -func (x *ExchangeProviderSubjectTokenResponse) GetAccessToken() string { - if x != nil { - return x.AccessToken - } - return "" -} - -func (x *ExchangeProviderSubjectTokenResponse) GetExpiresAfter() *durationpb.Duration { - if x != nil { - return x.ExpiresAfter - } - return nil -} - -func (x *ExchangeProviderSubjectTokenResponse) GetTokenType() string { - if x != nil { - return x.TokenType - } - return "" -} - -// Update sandbox policy request. -type UpdateConfigRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Omit for global updates; otherwise select one named workspace. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,10,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // The new policy to apply. - // - // Sandbox scope (`global=false`): - // - only network_policies may differ from create-time - // policy; static fields must match version 1. - // - // Global scope (`global=true`): - // - applies to all sandboxes in full (no merge). - Policy *sandboxv1.SandboxPolicy `protobuf:"bytes,2,opt,name=policy,proto3" json:"policy,omitempty"` - // Optional single setting key to mutate. - SettingKey string `protobuf:"bytes,3,opt,name=setting_key,json=settingKey,proto3" json:"setting_key,omitempty"` - // Setting value for upsert operations. - SettingValue *sandboxv1.SettingValue `protobuf:"bytes,4,opt,name=setting_value,json=settingValue,proto3" json:"setting_value,omitempty"` - // Delete the setting key from scope. - // Sandbox-scoped deletes are rejected; only global delete is supported. - DeleteSetting bool `protobuf:"varint,5,opt,name=delete_setting,json=deleteSetting,proto3" json:"delete_setting,omitempty"` - // Apply mutation at gateway-global scope. - Global bool `protobuf:"varint,6,opt,name=global,proto3" json:"global,omitempty"` - // Batched incremental policy merge operations. Sandbox-scoped only. - MergeOperations []*PolicyMergeOperation `protobuf:"bytes,7,rep,name=merge_operations,json=mergeOperations,proto3" json:"merge_operations,omitempty"` - // Expected resource version for optimistic concurrency control (sandbox-scoped only). - // If 0, the server uses the current version (backward compatibility). - // If non-zero, the server validates that the sandbox's current resource_version - // matches this value before applying the mutation, returning ABORTED on mismatch. - // Ignored for global-scoped updates. - ExpectedResourceVersion uint64 `protobuf:"varint,8,opt,name=expected_resource_version,json=expectedResourceVersion,proto3" json:"expected_resource_version,omitempty"` - // Caller-provided annotations associated with a sandbox-scoped update. Values - // must not contain secrets; the gateway treats them as opaque metadata and does - // not interpret or verify their semantics. For policy updates, the gateway - // stores the annotations immutably with the revision and merges them into - // sandbox metadata as a convenience projection. For setting-only updates, it - // only merges them into sandbox metadata. Keys under the - // `internal.openshell.ai/` prefix are reserved for the gateway. - Annotations map[string]string `protobuf:"bytes,9,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Required for sandbox-scoped updates and empty for global updates. - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,11,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UpdateConfigRequest) Reset() { - *x = UpdateConfigRequest{} - mi := &file_openshell_proto_msgTypes[150] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UpdateConfigRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateConfigRequest) ProtoMessage() {} - -func (x *UpdateConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[150] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateConfigRequest.ProtoReflect.Descriptor instead. -func (*UpdateConfigRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{150} -} - -func (x *UpdateConfigRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *UpdateConfigRequest) GetPolicy() *sandboxv1.SandboxPolicy { - if x != nil { - return x.Policy - } - return nil -} - -func (x *UpdateConfigRequest) GetSettingKey() string { - if x != nil { - return x.SettingKey - } - return "" -} - -func (x *UpdateConfigRequest) GetSettingValue() *sandboxv1.SettingValue { - if x != nil { - return x.SettingValue - } - return nil -} - -func (x *UpdateConfigRequest) GetDeleteSetting() bool { - if x != nil { - return x.DeleteSetting - } - return false -} - -func (x *UpdateConfigRequest) GetGlobal() bool { - if x != nil { - return x.Global - } - return false -} - -func (x *UpdateConfigRequest) GetMergeOperations() []*PolicyMergeOperation { - if x != nil { - return x.MergeOperations - } - return nil -} - -func (x *UpdateConfigRequest) GetExpectedResourceVersion() uint64 { - if x != nil { - return x.ExpectedResourceVersion - } - return 0 -} - -func (x *UpdateConfigRequest) GetAnnotations() map[string]string { - if x != nil { - return x.Annotations - } - return nil -} - -func (x *UpdateConfigRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *UpdateConfigRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -type PolicyMergeOperation struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Operation: - // - // *PolicyMergeOperation_AddRule - // *PolicyMergeOperation_RemoveEndpoint - // *PolicyMergeOperation_RemoveRule - // *PolicyMergeOperation_AddDenyRules - // *PolicyMergeOperation_AddAllowRules - // *PolicyMergeOperation_RemoveBinary - Operation isPolicyMergeOperation_Operation `protobuf_oneof:"operation"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PolicyMergeOperation) Reset() { - *x = PolicyMergeOperation{} - mi := &file_openshell_proto_msgTypes[151] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PolicyMergeOperation) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PolicyMergeOperation) ProtoMessage() {} - -func (x *PolicyMergeOperation) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[151] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PolicyMergeOperation.ProtoReflect.Descriptor instead. -func (*PolicyMergeOperation) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{151} -} - -func (x *PolicyMergeOperation) GetOperation() isPolicyMergeOperation_Operation { - if x != nil { - return x.Operation - } - return nil -} - -func (x *PolicyMergeOperation) GetAddRule() *AddNetworkRule { - if x != nil { - if x, ok := x.Operation.(*PolicyMergeOperation_AddRule); ok { - return x.AddRule - } - } - return nil -} - -func (x *PolicyMergeOperation) GetRemoveEndpoint() *RemoveNetworkEndpoint { - if x != nil { - if x, ok := x.Operation.(*PolicyMergeOperation_RemoveEndpoint); ok { - return x.RemoveEndpoint - } - } - return nil -} - -func (x *PolicyMergeOperation) GetRemoveRule() *RemoveNetworkRule { - if x != nil { - if x, ok := x.Operation.(*PolicyMergeOperation_RemoveRule); ok { - return x.RemoveRule - } - } - return nil -} - -func (x *PolicyMergeOperation) GetAddDenyRules() *AddDenyRules { - if x != nil { - if x, ok := x.Operation.(*PolicyMergeOperation_AddDenyRules); ok { - return x.AddDenyRules - } - } - return nil -} - -func (x *PolicyMergeOperation) GetAddAllowRules() *AddAllowRules { - if x != nil { - if x, ok := x.Operation.(*PolicyMergeOperation_AddAllowRules); ok { - return x.AddAllowRules - } - } - return nil -} - -func (x *PolicyMergeOperation) GetRemoveBinary() *RemoveNetworkBinary { - if x != nil { - if x, ok := x.Operation.(*PolicyMergeOperation_RemoveBinary); ok { - return x.RemoveBinary - } - } - return nil -} - -type isPolicyMergeOperation_Operation interface { - isPolicyMergeOperation_Operation() -} - -type PolicyMergeOperation_AddRule struct { - AddRule *AddNetworkRule `protobuf:"bytes,1,opt,name=add_rule,json=addRule,proto3,oneof"` -} - -type PolicyMergeOperation_RemoveEndpoint struct { - RemoveEndpoint *RemoveNetworkEndpoint `protobuf:"bytes,2,opt,name=remove_endpoint,json=removeEndpoint,proto3,oneof"` -} - -type PolicyMergeOperation_RemoveRule struct { - RemoveRule *RemoveNetworkRule `protobuf:"bytes,3,opt,name=remove_rule,json=removeRule,proto3,oneof"` -} - -type PolicyMergeOperation_AddDenyRules struct { - AddDenyRules *AddDenyRules `protobuf:"bytes,4,opt,name=add_deny_rules,json=addDenyRules,proto3,oneof"` -} - -type PolicyMergeOperation_AddAllowRules struct { - AddAllowRules *AddAllowRules `protobuf:"bytes,5,opt,name=add_allow_rules,json=addAllowRules,proto3,oneof"` -} - -type PolicyMergeOperation_RemoveBinary struct { - RemoveBinary *RemoveNetworkBinary `protobuf:"bytes,6,opt,name=remove_binary,json=removeBinary,proto3,oneof"` -} - -func (*PolicyMergeOperation_AddRule) isPolicyMergeOperation_Operation() {} - -func (*PolicyMergeOperation_RemoveEndpoint) isPolicyMergeOperation_Operation() {} - -func (*PolicyMergeOperation_RemoveRule) isPolicyMergeOperation_Operation() {} - -func (*PolicyMergeOperation_AddDenyRules) isPolicyMergeOperation_Operation() {} - -func (*PolicyMergeOperation_AddAllowRules) isPolicyMergeOperation_Operation() {} - -func (*PolicyMergeOperation_RemoveBinary) isPolicyMergeOperation_Operation() {} - -type AddNetworkRule struct { - state protoimpl.MessageState `protogen:"open.v1"` - RuleName string `protobuf:"bytes,1,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - Rule *sandboxv1.NetworkPolicyRule `protobuf:"bytes,2,opt,name=rule,proto3" json:"rule,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AddNetworkRule) Reset() { - *x = AddNetworkRule{} - mi := &file_openshell_proto_msgTypes[152] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AddNetworkRule) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddNetworkRule) ProtoMessage() {} - -func (x *AddNetworkRule) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[152] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddNetworkRule.ProtoReflect.Descriptor instead. -func (*AddNetworkRule) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{152} -} - -func (x *AddNetworkRule) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -func (x *AddNetworkRule) GetRule() *sandboxv1.NetworkPolicyRule { - if x != nil { - return x.Rule - } - return nil -} - -type RemoveNetworkEndpoint struct { - state protoimpl.MessageState `protogen:"open.v1"` - RuleName string `protobuf:"bytes,1,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"` - Port uint32 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RemoveNetworkEndpoint) Reset() { - *x = RemoveNetworkEndpoint{} - mi := &file_openshell_proto_msgTypes[153] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RemoveNetworkEndpoint) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveNetworkEndpoint) ProtoMessage() {} - -func (x *RemoveNetworkEndpoint) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[153] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveNetworkEndpoint.ProtoReflect.Descriptor instead. -func (*RemoveNetworkEndpoint) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{153} -} - -func (x *RemoveNetworkEndpoint) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -func (x *RemoveNetworkEndpoint) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *RemoveNetworkEndpoint) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - -type RemoveNetworkRule struct { - state protoimpl.MessageState `protogen:"open.v1"` - RuleName string `protobuf:"bytes,1,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RemoveNetworkRule) Reset() { - *x = RemoveNetworkRule{} - mi := &file_openshell_proto_msgTypes[154] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RemoveNetworkRule) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveNetworkRule) ProtoMessage() {} - -func (x *RemoveNetworkRule) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[154] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveNetworkRule.ProtoReflect.Descriptor instead. -func (*RemoveNetworkRule) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{154} -} - -func (x *RemoveNetworkRule) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -// Exact endpoint and complete authorization scope affected by an L7 append. -// All ports and binaries must match the stored target; omitted scope is invalid. -type L7RuleTarget struct { - state protoimpl.MessageState `protogen:"open.v1"` - RuleName string `protobuf:"bytes,1,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"` - Ports []uint32 `protobuf:"varint,3,rep,packed,name=ports,proto3" json:"ports,omitempty"` - // An absent path requires a unique endpoint. An empty path selects an - // endpoint without a path selector. This is not the appended request path. - Path *string `protobuf:"bytes,4,opt,name=path,proto3,oneof" json:"path,omitempty"` - // Declare either a nonempty binary list or any_binary, never both. - Binaries []*sandboxv1.NetworkBinary `protobuf:"bytes,5,rep,name=binaries,proto3" json:"binaries,omitempty"` - AnyBinary bool `protobuf:"varint,6,opt,name=any_binary,json=anyBinary,proto3" json:"any_binary,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *L7RuleTarget) Reset() { - *x = L7RuleTarget{} - mi := &file_openshell_proto_msgTypes[155] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *L7RuleTarget) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L7RuleTarget) ProtoMessage() {} - -func (x *L7RuleTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[155] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L7RuleTarget.ProtoReflect.Descriptor instead. -func (*L7RuleTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{155} -} - -func (x *L7RuleTarget) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -func (x *L7RuleTarget) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *L7RuleTarget) GetPorts() []uint32 { - if x != nil { - return x.Ports - } - return nil -} - -func (x *L7RuleTarget) GetPath() string { - if x != nil && x.Path != nil { - return *x.Path - } - return "" -} - -func (x *L7RuleTarget) GetBinaries() []*sandboxv1.NetworkBinary { - if x != nil { - return x.Binaries - } - return nil -} - -func (x *L7RuleTarget) GetAnyBinary() bool { - if x != nil { - return x.AnyBinary - } - return false -} - -type AddDenyRules struct { - state protoimpl.MessageState `protogen:"open.v1"` - DenyRules []*sandboxv1.L7DenyRule `protobuf:"bytes,3,rep,name=deny_rules,json=denyRules,proto3" json:"deny_rules,omitempty"` - Target *L7RuleTarget `protobuf:"bytes,4,opt,name=target,proto3" json:"target,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AddDenyRules) Reset() { - *x = AddDenyRules{} - mi := &file_openshell_proto_msgTypes[156] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AddDenyRules) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddDenyRules) ProtoMessage() {} - -func (x *AddDenyRules) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[156] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddDenyRules.ProtoReflect.Descriptor instead. -func (*AddDenyRules) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{156} -} - -func (x *AddDenyRules) GetDenyRules() []*sandboxv1.L7DenyRule { - if x != nil { - return x.DenyRules - } - return nil -} - -func (x *AddDenyRules) GetTarget() *L7RuleTarget { - if x != nil { - return x.Target - } - return nil -} - -type AddAllowRules struct { - state protoimpl.MessageState `protogen:"open.v1"` - Rules []*sandboxv1.L7Rule `protobuf:"bytes,3,rep,name=rules,proto3" json:"rules,omitempty"` - Target *L7RuleTarget `protobuf:"bytes,4,opt,name=target,proto3" json:"target,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AddAllowRules) Reset() { - *x = AddAllowRules{} - mi := &file_openshell_proto_msgTypes[157] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AddAllowRules) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddAllowRules) ProtoMessage() {} - -func (x *AddAllowRules) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[157] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddAllowRules.ProtoReflect.Descriptor instead. -func (*AddAllowRules) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{157} -} - -func (x *AddAllowRules) GetRules() []*sandboxv1.L7Rule { - if x != nil { - return x.Rules - } - return nil -} - -func (x *AddAllowRules) GetTarget() *L7RuleTarget { - if x != nil { - return x.Target - } - return nil -} - -type RemoveNetworkBinary struct { - state protoimpl.MessageState `protogen:"open.v1"` - RuleName string `protobuf:"bytes,1,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - BinaryPath string `protobuf:"bytes,2,opt,name=binary_path,json=binaryPath,proto3" json:"binary_path,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RemoveNetworkBinary) Reset() { - *x = RemoveNetworkBinary{} - mi := &file_openshell_proto_msgTypes[158] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RemoveNetworkBinary) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveNetworkBinary) ProtoMessage() {} - -func (x *RemoveNetworkBinary) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[158] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveNetworkBinary.ProtoReflect.Descriptor instead. -func (*RemoveNetworkBinary) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{158} -} - -func (x *RemoveNetworkBinary) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -func (x *RemoveNetworkBinary) GetBinaryPath() string { - if x != nil { - return x.BinaryPath - } - return "" -} - -// Update sandbox policy response. -type UpdateConfigResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Assigned policy version (monotonically increasing per sandbox). - Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - // SHA-256 hash of the serialized policy payload. - PolicyHash string `protobuf:"bytes,2,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - // Settings revision for the scope that was modified. - SettingsRevision uint64 `protobuf:"varint,3,opt,name=settings_revision,json=settingsRevision,proto3" json:"settings_revision,omitempty"` - // True when a setting delete operation removed an existing key. - Deleted bool `protobuf:"varint,4,opt,name=deleted,proto3" json:"deleted,omitempty"` - // Sandbox metadata annotations after the update. Empty for global updates. - Annotations map[string]string `protobuf:"bytes,5,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UpdateConfigResponse) Reset() { - *x = UpdateConfigResponse{} - mi := &file_openshell_proto_msgTypes[159] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UpdateConfigResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateConfigResponse) ProtoMessage() {} - -func (x *UpdateConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[159] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateConfigResponse.ProtoReflect.Descriptor instead. -func (*UpdateConfigResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{159} -} - -func (x *UpdateConfigResponse) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *UpdateConfigResponse) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *UpdateConfigResponse) GetSettingsRevision() uint64 { - if x != nil { - return x.SettingsRevision - } - return 0 -} - -func (x *UpdateConfigResponse) GetDeleted() bool { - if x != nil { - return x.Deleted - } - return false -} - -func (x *UpdateConfigResponse) GetAnnotations() map[string]string { - if x != nil { - return x.Annotations - } - return nil -} - -// Get sandbox policy status request. -type GetSandboxPolicyStatusRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Omit for global queries; otherwise select one named workspace. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,4,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // The specific policy version to query. 0 means latest. - Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` - // Query global policy revisions instead of a sandbox-scoped one. - Global bool `protobuf:"varint,3,opt,name=global,proto3" json:"global,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxPolicyStatusRequest) Reset() { - *x = GetSandboxPolicyStatusRequest{} - mi := &file_openshell_proto_msgTypes[160] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxPolicyStatusRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxPolicyStatusRequest) ProtoMessage() {} - -func (x *GetSandboxPolicyStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[160] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxPolicyStatusRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxPolicyStatusRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{160} -} - -func (x *GetSandboxPolicyStatusRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetSandboxPolicyStatusRequest) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *GetSandboxPolicyStatusRequest) GetGlobal() bool { - if x != nil { - return x.Global - } - return false -} - -func (x *GetSandboxPolicyStatusRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -// Get sandbox policy status response. -type GetSandboxPolicyStatusResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The queried policy revision. - Revision *SandboxPolicyRevision `protobuf:"bytes,1,opt,name=revision,proto3" json:"revision,omitempty"` - // The currently active (loaded) policy version for this sandbox. - ActiveVersion uint32 `protobuf:"varint,2,opt,name=active_version,json=activeVersion,proto3" json:"active_version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxPolicyStatusResponse) Reset() { - *x = GetSandboxPolicyStatusResponse{} - mi := &file_openshell_proto_msgTypes[161] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxPolicyStatusResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxPolicyStatusResponse) ProtoMessage() {} - -func (x *GetSandboxPolicyStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[161] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxPolicyStatusResponse.ProtoReflect.Descriptor instead. -func (*GetSandboxPolicyStatusResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{161} -} - -func (x *GetSandboxPolicyStatusResponse) GetRevision() *SandboxPolicyRevision { - if x != nil { - return x.Revision - } - return nil -} - -func (x *GetSandboxPolicyStatusResponse) GetActiveVersion() uint32 { - if x != nil { - return x.ActiveVersion - } - return 0 -} - -// List sandbox policies request. -type ListSandboxPoliciesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Omit for global queries; otherwise select one named workspace. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,5,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // The maximum number of revisions to return. Zero uses 100. Values above - // 1000 are coerced to 1000; negative values are invalid. - PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - // Token from a previous ListSandboxPolicies response. All other request - // parameters except page_size must match the request that produced it. - PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` - // List global policy revisions instead of sandbox-scoped ones. - Global bool `protobuf:"varint,4,opt,name=global,proto3" json:"global,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxPoliciesRequest) Reset() { - *x = ListSandboxPoliciesRequest{} - mi := &file_openshell_proto_msgTypes[162] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxPoliciesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxPoliciesRequest) ProtoMessage() {} - -func (x *ListSandboxPoliciesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[162] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxPoliciesRequest.ProtoReflect.Descriptor instead. -func (*ListSandboxPoliciesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{162} -} - -func (x *ListSandboxPoliciesRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ListSandboxPoliciesRequest) GetPageSize() int32 { - if x != nil { - return x.PageSize - } - return 0 -} - -func (x *ListSandboxPoliciesRequest) GetPageToken() string { - if x != nil { - return x.PageToken - } - return "" -} - -func (x *ListSandboxPoliciesRequest) GetGlobal() bool { - if x != nil { - return x.Global - } - return false -} - -func (x *ListSandboxPoliciesRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -// List sandbox policies response. -type ListSandboxPoliciesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Invalid historical payloads remain visible as failed projections so one - // legacy row cannot hide the rest of the policy history. - Revisions []*SandboxPolicyRevision `protobuf:"bytes,1,rep,name=revisions,proto3" json:"revisions,omitempty"` - // Token for the next page. Empty when there are no subsequent pages. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxPoliciesResponse) Reset() { - *x = ListSandboxPoliciesResponse{} - mi := &file_openshell_proto_msgTypes[163] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxPoliciesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxPoliciesResponse) ProtoMessage() {} - -func (x *ListSandboxPoliciesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[163] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxPoliciesResponse.ProtoReflect.Descriptor instead. -func (*ListSandboxPoliciesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{163} -} - -func (x *ListSandboxPoliciesResponse) GetRevisions() []*SandboxPolicyRevision { - if x != nil { - return x.Revisions - } - return nil -} - -func (x *ListSandboxPoliciesResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -// Report policy load status (called by sandbox runtime after reload attempt). -type ReportPolicyStatusRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox id. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // The policy version that was attempted. - Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` - // Load result status. - Status PolicyStatus `protobuf:"varint,3,opt,name=status,proto3,enum=openshell.v1.PolicyStatus" json:"status,omitempty"` - // Error message if status is FAILED. - LoadError string `protobuf:"bytes,4,opt,name=load_error,json=loadError,proto3" json:"load_error,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReportPolicyStatusRequest) Reset() { - *x = ReportPolicyStatusRequest{} - mi := &file_openshell_proto_msgTypes[164] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReportPolicyStatusRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportPolicyStatusRequest) ProtoMessage() {} - -func (x *ReportPolicyStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[164] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportPolicyStatusRequest.ProtoReflect.Descriptor instead. -func (*ReportPolicyStatusRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{164} -} - -func (x *ReportPolicyStatusRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *ReportPolicyStatusRequest) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *ReportPolicyStatusRequest) GetStatus() PolicyStatus { - if x != nil { - return x.Status - } - return PolicyStatus_POLICY_STATUS_UNSPECIFIED -} - -func (x *ReportPolicyStatusRequest) GetLoadError() string { - if x != nil { - return x.LoadError - } - return "" -} - -// Report policy status response. -type ReportPolicyStatusResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReportPolicyStatusResponse) Reset() { - *x = ReportPolicyStatusResponse{} - mi := &file_openshell_proto_msgTypes[165] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReportPolicyStatusResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportPolicyStatusResponse) ProtoMessage() {} - -func (x *ReportPolicyStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[165] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportPolicyStatusResponse.ProtoReflect.Descriptor instead. -func (*ReportPolicyStatusResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{165} -} - -type SandboxConfigurationAdmission struct { - state protoimpl.MessageState `protogen:"open.v1"` - InstanceId string `protobuf:"bytes,1,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` - State ConfigurationAdmissionState `protobuf:"varint,2,opt,name=state,proto3,enum=openshell.v1.ConfigurationAdmissionState" json:"state,omitempty"` - PolicyVersion uint32 `protobuf:"varint,3,opt,name=policy_version,json=policyVersion,proto3" json:"policy_version,omitempty"` - PolicyHash string `protobuf:"bytes,4,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - ConfigRevision uint64 `protobuf:"varint,5,opt,name=config_revision,json=configRevision,proto3" json:"config_revision,omitempty"` - ProviderEnvRevision uint64 `protobuf:"varint,6,opt,name=provider_env_revision,json=providerEnvRevision,proto3" json:"provider_env_revision,omitempty"` - Error string `protobuf:"bytes,7,opt,name=error,proto3" json:"error,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxConfigurationAdmission) Reset() { - *x = SandboxConfigurationAdmission{} - mi := &file_openshell_proto_msgTypes[166] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxConfigurationAdmission) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxConfigurationAdmission) ProtoMessage() {} - -func (x *SandboxConfigurationAdmission) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[166] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxConfigurationAdmission.ProtoReflect.Descriptor instead. -func (*SandboxConfigurationAdmission) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{166} -} - -func (x *SandboxConfigurationAdmission) GetInstanceId() string { - if x != nil { - return x.InstanceId - } - return "" -} - -func (x *SandboxConfigurationAdmission) GetState() ConfigurationAdmissionState { - if x != nil { - return x.State - } - return ConfigurationAdmissionState_CONFIGURATION_ADMISSION_STATE_UNSPECIFIED -} - -func (x *SandboxConfigurationAdmission) GetPolicyVersion() uint32 { - if x != nil { - return x.PolicyVersion - } - return 0 -} - -func (x *SandboxConfigurationAdmission) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *SandboxConfigurationAdmission) GetConfigRevision() uint64 { - if x != nil { - return x.ConfigRevision - } - return 0 -} - -func (x *SandboxConfigurationAdmission) GetProviderEnvRevision() uint64 { - if x != nil { - return x.ProviderEnvRevision - } - return 0 -} - -func (x *SandboxConfigurationAdmission) GetError() string { - if x != nil { - return x.Error - } - return "" -} - -type ReportSandboxConfigurationRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - Admission *SandboxConfigurationAdmission `protobuf:"bytes,2,opt,name=admission,proto3" json:"admission,omitempty"` - // Pending registration replaces only this previously observed instance. - ExpectedInstanceId string `protobuf:"bytes,3,opt,name=expected_instance_id,json=expectedInstanceId,proto3" json:"expected_instance_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReportSandboxConfigurationRequest) Reset() { - *x = ReportSandboxConfigurationRequest{} - mi := &file_openshell_proto_msgTypes[167] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReportSandboxConfigurationRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportSandboxConfigurationRequest) ProtoMessage() {} - -func (x *ReportSandboxConfigurationRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[167] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportSandboxConfigurationRequest.ProtoReflect.Descriptor instead. -func (*ReportSandboxConfigurationRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{167} -} - -func (x *ReportSandboxConfigurationRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *ReportSandboxConfigurationRequest) GetAdmission() *SandboxConfigurationAdmission { - if x != nil { - return x.Admission - } - return nil -} - -func (x *ReportSandboxConfigurationRequest) GetExpectedInstanceId() string { - if x != nil { - return x.ExpectedInstanceId - } - return "" -} - -type ReportSandboxConfigurationResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReportSandboxConfigurationResponse) Reset() { - *x = ReportSandboxConfigurationResponse{} - mi := &file_openshell_proto_msgTypes[168] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReportSandboxConfigurationResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportSandboxConfigurationResponse) ProtoMessage() {} - -func (x *ReportSandboxConfigurationResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[168] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportSandboxConfigurationResponse.ProtoReflect.Descriptor instead. -func (*ReportSandboxConfigurationResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{168} -} - -// A versioned policy revision with metadata. -type SandboxPolicyRevision struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Policy version (monotonically increasing per sandbox). - Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - // SHA-256 hash of the canonical serialized policy payload. Empty in a - // ListSandboxPolicies projection when the stored payload is invalid under - // the current schema and therefore has no trusted canonical identity. - PolicyHash string `protobuf:"bytes,2,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - // Load status of this revision. ListSandboxPolicies reports FAILED when a - // stored historical payload is invalid under the current schema, regardless - // of its persisted sandbox load status. - Status PolicyStatus `protobuf:"varint,3,opt,name=status,proto3,enum=openshell.v1.PolicyStatus" json:"status,omitempty"` - // Sandbox load error, or the schema-validation diagnostic for an invalid - // historical row returned by ListSandboxPolicies. - LoadError string `protobuf:"bytes,4,opt,name=load_error,json=loadError,proto3" json:"load_error,omitempty"` - // Time when this revision was created. - CreatedTime *timestamppb.Timestamp `protobuf:"bytes,105,opt,name=created_time,json=createdTime,proto3" json:"created_time,omitempty"` - // Time when this revision was loaded by the sandbox. Absent if not loaded. - LoadedTime *timestamppb.Timestamp `protobuf:"bytes,106,opt,name=loaded_time,json=loadedTime,proto3" json:"loaded_time,omitempty"` - // The full policy (only populated when explicitly requested). - Policy *sandboxv1.SandboxPolicy `protobuf:"bytes,7,opt,name=policy,proto3" json:"policy,omitempty"` - // Immutable provenance supplied with this policy revision. - Provenance map[string]string `protobuf:"bytes,8,rep,name=provenance,proto3" json:"provenance,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxPolicyRevision) Reset() { - *x = SandboxPolicyRevision{} - mi := &file_openshell_proto_msgTypes[169] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxPolicyRevision) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxPolicyRevision) ProtoMessage() {} - -func (x *SandboxPolicyRevision) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[169] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxPolicyRevision.ProtoReflect.Descriptor instead. -func (*SandboxPolicyRevision) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{169} -} - -func (x *SandboxPolicyRevision) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *SandboxPolicyRevision) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *SandboxPolicyRevision) GetStatus() PolicyStatus { - if x != nil { - return x.Status - } - return PolicyStatus_POLICY_STATUS_UNSPECIFIED -} - -func (x *SandboxPolicyRevision) GetLoadError() string { - if x != nil { - return x.LoadError - } - return "" -} - -func (x *SandboxPolicyRevision) GetCreatedTime() *timestamppb.Timestamp { - if x != nil { - return x.CreatedTime - } - return nil -} - -func (x *SandboxPolicyRevision) GetLoadedTime() *timestamppb.Timestamp { - if x != nil { - return x.LoadedTime - } - return nil -} - -func (x *SandboxPolicyRevision) GetPolicy() *sandboxv1.SandboxPolicy { - if x != nil { - return x.Policy - } - return nil -} - -func (x *SandboxPolicyRevision) GetProvenance() map[string]string { - if x != nil { - return x.Provenance - } - return nil -} - -// Get sandbox logs request (one-shot fetch). -type GetSandboxLogsRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,6,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Canonical sandbox name. - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Maximum number of log lines to return. 0 means use default (2000). - Lines uint32 `protobuf:"varint,2,opt,name=lines,proto3" json:"lines,omitempty"` - // Only include logs at or after this time. Absence means no filter. - SinceTime *timestamppb.Timestamp `protobuf:"bytes,103,opt,name=since_time,json=sinceTime,proto3" json:"since_time,omitempty"` - // Filter by log source (e.g. "gateway", "sandbox"). Empty means all sources. - Sources []string `protobuf:"bytes,4,rep,name=sources,proto3" json:"sources,omitempty"` - // Minimum log level to include (e.g. "INFO", "WARN", "ERROR"). Empty means all levels. - MinLevel string `protobuf:"bytes,5,opt,name=min_level,json=minLevel,proto3" json:"min_level,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxLogsRequest) Reset() { - *x = GetSandboxLogsRequest{} - mi := &file_openshell_proto_msgTypes[170] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxLogsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxLogsRequest) ProtoMessage() {} - -func (x *GetSandboxLogsRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[170] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxLogsRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxLogsRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{170} -} - -func (x *GetSandboxLogsRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetSandboxLogsRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *GetSandboxLogsRequest) GetLines() uint32 { - if x != nil { - return x.Lines - } - return 0 -} - -func (x *GetSandboxLogsRequest) GetSinceTime() *timestamppb.Timestamp { - if x != nil { - return x.SinceTime - } - return nil -} - -func (x *GetSandboxLogsRequest) GetSources() []string { - if x != nil { - return x.Sources - } - return nil -} - -func (x *GetSandboxLogsRequest) GetMinLevel() string { - if x != nil { - return x.MinLevel - } - return "" -} - -// Batch of log lines pushed from sandbox to server. -type PushSandboxLogsRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The sandbox ID. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Log lines to ingest. - Logs []*SandboxLogLine `protobuf:"bytes,2,rep,name=logs,proto3" json:"logs,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PushSandboxLogsRequest) Reset() { - *x = PushSandboxLogsRequest{} - mi := &file_openshell_proto_msgTypes[171] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PushSandboxLogsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PushSandboxLogsRequest) ProtoMessage() {} - -func (x *PushSandboxLogsRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[171] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PushSandboxLogsRequest.ProtoReflect.Descriptor instead. -func (*PushSandboxLogsRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{171} -} - -func (x *PushSandboxLogsRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *PushSandboxLogsRequest) GetLogs() []*SandboxLogLine { - if x != nil { - return x.Logs - } - return nil -} - -// Push sandbox logs response. -type PushSandboxLogsResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PushSandboxLogsResponse) Reset() { - *x = PushSandboxLogsResponse{} - mi := &file_openshell_proto_msgTypes[172] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PushSandboxLogsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PushSandboxLogsResponse) ProtoMessage() {} - -func (x *PushSandboxLogsResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[172] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PushSandboxLogsResponse.ProtoReflect.Descriptor instead. -func (*PushSandboxLogsResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{172} -} - -// Get sandbox logs response. -type GetSandboxLogsResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Log lines in chronological order. - Logs []*SandboxLogLine `protobuf:"bytes,1,rep,name=logs,proto3" json:"logs,omitempty"` - // Total number of lines in the server's buffer for this sandbox. - BufferTotal uint32 `protobuf:"varint,2,opt,name=buffer_total,json=bufferTotal,proto3" json:"buffer_total,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxLogsResponse) Reset() { - *x = GetSandboxLogsResponse{} - mi := &file_openshell_proto_msgTypes[173] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxLogsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxLogsResponse) ProtoMessage() {} - -func (x *GetSandboxLogsResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[173] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxLogsResponse.ProtoReflect.Descriptor instead. -func (*GetSandboxLogsResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{173} -} - -func (x *GetSandboxLogsResponse) GetLogs() []*SandboxLogLine { - if x != nil { - return x.Logs - } - return nil -} - -func (x *GetSandboxLogsResponse) GetBufferTotal() uint32 { - if x != nil { - return x.BufferTotal - } - return 0 -} - -// Envelope for supervisor-to-gateway messages on the ConnectSupervisor stream. -type SupervisorMessage struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *SupervisorMessage_Hello - // *SupervisorMessage_Heartbeat - // *SupervisorMessage_RelayOpenResult - // *SupervisorMessage_RelayClose - // *SupervisorMessage_ConfigUpdateResult - Payload isSupervisorMessage_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SupervisorMessage) Reset() { - *x = SupervisorMessage{} - mi := &file_openshell_proto_msgTypes[174] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SupervisorMessage) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SupervisorMessage) ProtoMessage() {} - -func (x *SupervisorMessage) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[174] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SupervisorMessage.ProtoReflect.Descriptor instead. -func (*SupervisorMessage) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{174} -} - -func (x *SupervisorMessage) GetPayload() isSupervisorMessage_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *SupervisorMessage) GetHello() *SupervisorHello { - if x != nil { - if x, ok := x.Payload.(*SupervisorMessage_Hello); ok { - return x.Hello - } - } - return nil -} - -func (x *SupervisorMessage) GetHeartbeat() *SupervisorHeartbeat { - if x != nil { - if x, ok := x.Payload.(*SupervisorMessage_Heartbeat); ok { - return x.Heartbeat - } - } - return nil -} - -func (x *SupervisorMessage) GetRelayOpenResult() *RelayOpenResult { - if x != nil { - if x, ok := x.Payload.(*SupervisorMessage_RelayOpenResult); ok { - return x.RelayOpenResult - } - } - return nil -} - -func (x *SupervisorMessage) GetRelayClose() *RelayClose { - if x != nil { - if x, ok := x.Payload.(*SupervisorMessage_RelayClose); ok { - return x.RelayClose - } - } - return nil -} - -func (x *SupervisorMessage) GetConfigUpdateResult() *ConfigUpdateResult { - if x != nil { - if x, ok := x.Payload.(*SupervisorMessage_ConfigUpdateResult); ok { - return x.ConfigUpdateResult - } - } - return nil -} - -type isSupervisorMessage_Payload interface { - isSupervisorMessage_Payload() -} - -type SupervisorMessage_Hello struct { - Hello *SupervisorHello `protobuf:"bytes,1,opt,name=hello,proto3,oneof"` -} - -type SupervisorMessage_Heartbeat struct { - Heartbeat *SupervisorHeartbeat `protobuf:"bytes,2,opt,name=heartbeat,proto3,oneof"` -} - -type SupervisorMessage_RelayOpenResult struct { - RelayOpenResult *RelayOpenResult `protobuf:"bytes,3,opt,name=relay_open_result,json=relayOpenResult,proto3,oneof"` -} - -type SupervisorMessage_RelayClose struct { - RelayClose *RelayClose `protobuf:"bytes,4,opt,name=relay_close,json=relayClose,proto3,oneof"` -} - -type SupervisorMessage_ConfigUpdateResult struct { - ConfigUpdateResult *ConfigUpdateResult `protobuf:"bytes,5,opt,name=config_update_result,json=configUpdateResult,proto3,oneof"` -} - -func (*SupervisorMessage_Hello) isSupervisorMessage_Payload() {} - -func (*SupervisorMessage_Heartbeat) isSupervisorMessage_Payload() {} - -func (*SupervisorMessage_RelayOpenResult) isSupervisorMessage_Payload() {} - -func (*SupervisorMessage_RelayClose) isSupervisorMessage_Payload() {} - -func (*SupervisorMessage_ConfigUpdateResult) isSupervisorMessage_Payload() {} - -// Envelope for gateway-to-supervisor messages on the ConnectSupervisor stream. -type GatewayMessage struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *GatewayMessage_SessionAccepted - // *GatewayMessage_SessionRejected - // *GatewayMessage_Heartbeat - // *GatewayMessage_RelayOpen - // *GatewayMessage_RelayClose - // *GatewayMessage_SessionRedirect - // *GatewayMessage_ConfigUpdate - Payload isGatewayMessage_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GatewayMessage) Reset() { - *x = GatewayMessage{} - mi := &file_openshell_proto_msgTypes[175] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GatewayMessage) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GatewayMessage) ProtoMessage() {} - -func (x *GatewayMessage) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[175] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GatewayMessage.ProtoReflect.Descriptor instead. -func (*GatewayMessage) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{175} -} - -func (x *GatewayMessage) GetPayload() isGatewayMessage_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *GatewayMessage) GetSessionAccepted() *SessionAccepted { - if x != nil { - if x, ok := x.Payload.(*GatewayMessage_SessionAccepted); ok { - return x.SessionAccepted - } - } - return nil -} - -func (x *GatewayMessage) GetSessionRejected() *SessionRejected { - if x != nil { - if x, ok := x.Payload.(*GatewayMessage_SessionRejected); ok { - return x.SessionRejected - } - } - return nil -} - -func (x *GatewayMessage) GetHeartbeat() *GatewayHeartbeat { - if x != nil { - if x, ok := x.Payload.(*GatewayMessage_Heartbeat); ok { - return x.Heartbeat - } - } - return nil -} - -func (x *GatewayMessage) GetRelayOpen() *RelayOpen { - if x != nil { - if x, ok := x.Payload.(*GatewayMessage_RelayOpen); ok { - return x.RelayOpen - } - } - return nil -} - -func (x *GatewayMessage) GetRelayClose() *RelayClose { - if x != nil { - if x, ok := x.Payload.(*GatewayMessage_RelayClose); ok { - return x.RelayClose - } - } - return nil -} - -func (x *GatewayMessage) GetSessionRedirect() *SessionRedirect { - if x != nil { - if x, ok := x.Payload.(*GatewayMessage_SessionRedirect); ok { - return x.SessionRedirect - } - } - return nil -} - -func (x *GatewayMessage) GetConfigUpdate() *ConfigUpdate { - if x != nil { - if x, ok := x.Payload.(*GatewayMessage_ConfigUpdate); ok { - return x.ConfigUpdate - } - } - return nil -} - -type isGatewayMessage_Payload interface { - isGatewayMessage_Payload() -} - -type GatewayMessage_SessionAccepted struct { - SessionAccepted *SessionAccepted `protobuf:"bytes,1,opt,name=session_accepted,json=sessionAccepted,proto3,oneof"` -} - -type GatewayMessage_SessionRejected struct { - SessionRejected *SessionRejected `protobuf:"bytes,2,opt,name=session_rejected,json=sessionRejected,proto3,oneof"` -} - -type GatewayMessage_Heartbeat struct { - Heartbeat *GatewayHeartbeat `protobuf:"bytes,3,opt,name=heartbeat,proto3,oneof"` -} - -type GatewayMessage_RelayOpen struct { - RelayOpen *RelayOpen `protobuf:"bytes,4,opt,name=relay_open,json=relayOpen,proto3,oneof"` -} - -type GatewayMessage_RelayClose struct { - RelayClose *RelayClose `protobuf:"bytes,5,opt,name=relay_close,json=relayClose,proto3,oneof"` -} - -type GatewayMessage_SessionRedirect struct { - SessionRedirect *SessionRedirect `protobuf:"bytes,6,opt,name=session_redirect,json=sessionRedirect,proto3,oneof"` -} - -type GatewayMessage_ConfigUpdate struct { - ConfigUpdate *ConfigUpdate `protobuf:"bytes,7,opt,name=config_update,json=configUpdate,proto3,oneof"` -} - -func (*GatewayMessage_SessionAccepted) isGatewayMessage_Payload() {} - -func (*GatewayMessage_SessionRejected) isGatewayMessage_Payload() {} - -func (*GatewayMessage_Heartbeat) isGatewayMessage_Payload() {} - -func (*GatewayMessage_RelayOpen) isGatewayMessage_Payload() {} - -func (*GatewayMessage_RelayClose) isGatewayMessage_Payload() {} - -func (*GatewayMessage_SessionRedirect) isGatewayMessage_Payload() {} - -func (*GatewayMessage_ConfigUpdate) isGatewayMessage_Payload() {} - -// Supervisor identifies itself and the sandbox it manages. -type SupervisorHello struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox ID this supervisor manages. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Supervisor instance ID (e.g. boot id or process epoch). - InstanceId string `protobuf:"bytes,2,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` - // Monotonic counter scoped to instance_id. Incremented for each reconnect so - // gateways can distinguish a fresh supervisor connection from stale cleanup. - ConnectionEpoch uint64 `protobuf:"varint,3,opt,name=connection_epoch,json=connectionEpoch,proto3" json:"connection_epoch,omitempty"` - // The supervisor can report credential, policy, and launch-environment installation. - SupportsProviderReadiness bool `protobuf:"varint,4,opt,name=supports_provider_readiness,json=supportsProviderReadiness,proto3" json:"supports_provider_readiness,omitempty"` - // Set when this connection follows a SessionRedirect, or dials the replica - // serving the sandbox's shared supervisor. The receiving gateway serves a - // redirected hello locally instead of redirecting again, so a disagreement - // about membership cannot bounce the supervisor between replicas. A gateway - // that is shutting down may still redirect it. - Redirected bool `protobuf:"varint,5,opt,name=redirected,proto3" json:"redirected,omitempty"` - // The supervisor understands SessionRedirect. Gateways never redirect a - // supervisor that does not set this. - SupportsSessionRedirect bool `protobuf:"varint,6,opt,name=supports_session_redirect,json=supportsSessionRedirect,proto3" json:"supports_session_redirect,omitempty"` - // The supervisor can receive its configuration over this stream instead of - // polling for it: it applies the bootstrap ConfigUpdate and later - // ConfigUpdate payloads, and answers each with its result and admission. - // Gateways push configuration only to supervisors that set this. - SupportsConfigPush bool `protobuf:"varint,7,opt,name=supports_config_push,json=supportsConfigPush,proto3" json:"supports_config_push,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SupervisorHello) Reset() { - *x = SupervisorHello{} - mi := &file_openshell_proto_msgTypes[176] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SupervisorHello) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SupervisorHello) ProtoMessage() {} - -func (x *SupervisorHello) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[176] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SupervisorHello.ProtoReflect.Descriptor instead. -func (*SupervisorHello) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{176} -} - -func (x *SupervisorHello) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *SupervisorHello) GetInstanceId() string { - if x != nil { - return x.InstanceId - } - return "" -} - -func (x *SupervisorHello) GetConnectionEpoch() uint64 { - if x != nil { - return x.ConnectionEpoch - } - return 0 -} - -func (x *SupervisorHello) GetSupportsProviderReadiness() bool { - if x != nil { - return x.SupportsProviderReadiness - } - return false -} - -func (x *SupervisorHello) GetRedirected() bool { - if x != nil { - return x.Redirected - } - return false -} - -func (x *SupervisorHello) GetSupportsSessionRedirect() bool { - if x != nil { - return x.SupportsSessionRedirect - } - return false -} - -func (x *SupervisorHello) GetSupportsConfigPush() bool { - if x != nil { - return x.SupportsConfigPush - } - return false -} - -// Gateway accepts the supervisor session. -type SessionAccepted struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Gateway-assigned session ID for this connection. - SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` - // The session's first configuration update. Present exactly when - // config_push_enabled is set. - Bootstrap *ConfigUpdate `protobuf:"bytes,3,opt,name=bootstrap,proto3" json:"bootstrap,omitempty"` - // The gateway pushes configuration authoritatively on this stream instead - // of the supervisor polling for it: the bootstrap is required, ConfigUpdate - // payloads are applied and acknowledged, and the supervisor stops polling. - // Set only for supervisors that set SupervisorHello.supports_config_push. - ConfigPushEnabled bool `protobuf:"varint,4,opt,name=config_push_enabled,json=configPushEnabled,proto3" json:"config_push_enabled,omitempty"` - // Recommended heartbeat interval. - HeartbeatInterval *durationpb.Duration `protobuf:"bytes,102,opt,name=heartbeat_interval,json=heartbeatInterval,proto3" json:"heartbeat_interval,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SessionAccepted) Reset() { - *x = SessionAccepted{} - mi := &file_openshell_proto_msgTypes[177] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SessionAccepted) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SessionAccepted) ProtoMessage() {} - -func (x *SessionAccepted) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[177] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SessionAccepted.ProtoReflect.Descriptor instead. -func (*SessionAccepted) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{177} -} - -func (x *SessionAccepted) GetSessionId() string { - if x != nil { - return x.SessionId - } - return "" -} - -func (x *SessionAccepted) GetBootstrap() *ConfigUpdate { - if x != nil { - return x.Bootstrap - } - return nil -} - -func (x *SessionAccepted) GetConfigPushEnabled() bool { - if x != nil { - return x.ConfigPushEnabled - } - return false -} - -func (x *SessionAccepted) GetHeartbeatInterval() *durationpb.Duration { - if x != nil { - return x.HeartbeatInterval - } - return nil -} - -// Complete gateway-owned configuration for a supervisor session. Each update -// replaces the previous one. The supervisor applies the two parts together -// and answers every update with one ConfigUpdateResult before the gateway -// sends the next. -type ConfigUpdate struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Opaque non-empty identifier scoped to the active supervisor session. - UpdateId string `protobuf:"bytes,1,opt,name=update_id,json=updateId,proto3" json:"update_id,omitempty"` - SandboxConfig *sandboxv1.SandboxConfigSnapshot `protobuf:"bytes,2,opt,name=sandbox_config,json=sandboxConfig,proto3" json:"sandbox_config,omitempty"` - // The provider environment the sandbox configuration names through its - // provider revision, attachment epoch, and policy hash. - ProviderEnvironment *ProviderEnvironmentSnapshot `protobuf:"bytes,3,opt,name=provider_environment,json=providerEnvironment,proto3" json:"provider_environment,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ConfigUpdate) Reset() { - *x = ConfigUpdate{} - mi := &file_openshell_proto_msgTypes[178] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ConfigUpdate) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConfigUpdate) ProtoMessage() {} - -func (x *ConfigUpdate) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[178] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConfigUpdate.ProtoReflect.Descriptor instead. -func (*ConfigUpdate) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{178} -} - -func (x *ConfigUpdate) GetUpdateId() string { - if x != nil { - return x.UpdateId - } - return "" -} - -func (x *ConfigUpdate) GetSandboxConfig() *sandboxv1.SandboxConfigSnapshot { - if x != nil { - return x.SandboxConfig - } - return nil -} - -func (x *ConfigUpdate) GetProviderEnvironment() *ProviderEnvironmentSnapshot { - if x != nil { - return x.ProviderEnvironment - } - return nil -} - -// Sanitized application failure. Messages must not contain configuration -// payloads, credentials, or provider values. -type ConfigApplyFailure struct { - state protoimpl.MessageState `protogen:"open.v1"` - Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` - Retryable bool `protobuf:"varint,3,opt,name=retryable,proto3" json:"retryable,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ConfigApplyFailure) Reset() { - *x = ConfigApplyFailure{} - mi := &file_openshell_proto_msgTypes[179] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ConfigApplyFailure) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConfigApplyFailure) ProtoMessage() {} - -func (x *ConfigApplyFailure) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[179] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConfigApplyFailure.ProtoReflect.Descriptor instead. -func (*ConfigApplyFailure) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{179} -} - -func (x *ConfigApplyFailure) GetCode() string { - if x != nil { - return x.Code - } - return "" -} - -func (x *ConfigApplyFailure) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *ConfigApplyFailure) GetRetryable() bool { - if x != nil { - return x.Retryable - } - return false -} - -// Application result for one ConfigUpdate, including the bootstrap. -type ConfigUpdateResult struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Echoes ConfigUpdate.update_id for session-local correlation. - UpdateId string `protobuf:"bytes,1,opt,name=update_id,json=updateId,proto3" json:"update_id,omitempty"` - Outcome ConfigApplyOutcome `protobuf:"varint,2,opt,name=outcome,proto3,enum=openshell.v1.ConfigApplyOutcome" json:"outcome,omitempty"` - // Why the update did not take effect. Omitted when it did. - Failure *ConfigApplyFailure `protobuf:"bytes,3,opt,name=failure,proto3" json:"failure,omitempty"` - // Admission state of the delivered sandbox configuration. - Admission *SandboxConfigurationAdmission `protobuf:"bytes,4,opt,name=admission,proto3" json:"admission,omitempty"` - // Load result of the sandbox policy revision the update changed, as a - // polling supervisor reports it with ReportPolicyStatus. Omitted when the - // update did not change the sandbox policy. - PolicyLoad *PolicyLoadResult `protobuf:"bytes,5,opt,name=policy_load,json=policyLoad,proto3" json:"policy_load,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ConfigUpdateResult) Reset() { - *x = ConfigUpdateResult{} - mi := &file_openshell_proto_msgTypes[180] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ConfigUpdateResult) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConfigUpdateResult) ProtoMessage() {} - -func (x *ConfigUpdateResult) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[180] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConfigUpdateResult.ProtoReflect.Descriptor instead. -func (*ConfigUpdateResult) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{180} -} - -func (x *ConfigUpdateResult) GetUpdateId() string { - if x != nil { - return x.UpdateId - } - return "" -} - -func (x *ConfigUpdateResult) GetOutcome() ConfigApplyOutcome { - if x != nil { - return x.Outcome - } - return ConfigApplyOutcome_CONFIG_APPLY_OUTCOME_UNSPECIFIED -} - -func (x *ConfigUpdateResult) GetFailure() *ConfigApplyFailure { - if x != nil { - return x.Failure - } - return nil -} - -func (x *ConfigUpdateResult) GetAdmission() *SandboxConfigurationAdmission { - if x != nil { - return x.Admission - } - return nil -} - -func (x *ConfigUpdateResult) GetPolicyLoad() *PolicyLoadResult { - if x != nil { - return x.PolicyLoad - } - return nil -} - -// Load result of one sandbox policy revision. -type PolicyLoadResult struct { - state protoimpl.MessageState `protogen:"open.v1"` - Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - Loaded bool `protobuf:"varint,2,opt,name=loaded,proto3" json:"loaded,omitempty"` - // Sanitized load error. Empty when loaded. - Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PolicyLoadResult) Reset() { - *x = PolicyLoadResult{} - mi := &file_openshell_proto_msgTypes[181] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PolicyLoadResult) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PolicyLoadResult) ProtoMessage() {} - -func (x *PolicyLoadResult) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[181] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PolicyLoadResult.ProtoReflect.Descriptor instead. -func (*PolicyLoadResult) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{181} -} - -func (x *PolicyLoadResult) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *PolicyLoadResult) GetLoaded() bool { - if x != nil { - return x.Loaded - } - return false -} - -func (x *PolicyLoadResult) GetError() string { - if x != nil { - return x.Error - } - return "" -} - -// Gateway rejects the supervisor session. -type SessionRejected struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Human-readable rejection reason. - Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SessionRejected) Reset() { - *x = SessionRejected{} - mi := &file_openshell_proto_msgTypes[182] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SessionRejected) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SessionRejected) ProtoMessage() {} - -func (x *SessionRejected) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[182] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SessionRejected.ProtoReflect.Descriptor instead. -func (*SessionRejected) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{182} -} - -func (x *SessionRejected) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -// Gateway declines to own this session and names the replica that should. -// -// Sent instead of SessionAccepted when the placement ring puts the sandbox on -// a different live replica, and to established sessions when their gateway -// shuts down. The supervisor reconnects to peer_endpoint once with -// SupervisorHello.redirected set, and falls back to its configured gateway -// address if that fails. -type SessionRedirect struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Peer endpoint of the replica that should own this sandbox. - PeerEndpoint string `protobuf:"bytes,1,opt,name=peer_endpoint,json=peerEndpoint,proto3" json:"peer_endpoint,omitempty"` - // Replica ID that should own this sandbox, for logging. - OwnerReplicaId string `protobuf:"bytes,2,opt,name=owner_replica_id,json=ownerReplicaId,proto3" json:"owner_replica_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SessionRedirect) Reset() { - *x = SessionRedirect{} - mi := &file_openshell_proto_msgTypes[183] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SessionRedirect) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SessionRedirect) ProtoMessage() {} - -func (x *SessionRedirect) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[183] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SessionRedirect.ProtoReflect.Descriptor instead. -func (*SessionRedirect) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{183} -} - -func (x *SessionRedirect) GetPeerEndpoint() string { - if x != nil { - return x.PeerEndpoint - } - return "" -} - -func (x *SessionRedirect) GetOwnerReplicaId() string { - if x != nil { - return x.OwnerReplicaId - } - return "" -} - -// Supervisor heartbeat. -type SupervisorHeartbeat struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SupervisorHeartbeat) Reset() { - *x = SupervisorHeartbeat{} - mi := &file_openshell_proto_msgTypes[184] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SupervisorHeartbeat) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SupervisorHeartbeat) ProtoMessage() {} - -func (x *SupervisorHeartbeat) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[184] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SupervisorHeartbeat.ProtoReflect.Descriptor instead. -func (*SupervisorHeartbeat) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{184} -} - -// Gateway heartbeat. -type GatewayHeartbeat struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GatewayHeartbeat) Reset() { - *x = GatewayHeartbeat{} - mi := &file_openshell_proto_msgTypes[185] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GatewayHeartbeat) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GatewayHeartbeat) ProtoMessage() {} - -func (x *GatewayHeartbeat) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[185] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GatewayHeartbeat.ProtoReflect.Descriptor instead. -func (*GatewayHeartbeat) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{185} -} - -// Terminal result reported before the supervisor shuts down. A successful RPC -// response confirms that the result was durably handled by the gateway. -type ReportMainProcessExitRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - InstanceId string `protobuf:"bytes,2,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` - // Normalized process result. Signal exits use 128 + signal number. - ExitCode int32 `protobuf:"varint,3,opt,name=exit_code,json=exitCode,proto3" json:"exit_code,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReportMainProcessExitRequest) Reset() { - *x = ReportMainProcessExitRequest{} - mi := &file_openshell_proto_msgTypes[186] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReportMainProcessExitRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportMainProcessExitRequest) ProtoMessage() {} - -func (x *ReportMainProcessExitRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[186] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportMainProcessExitRequest.ProtoReflect.Descriptor instead. -func (*ReportMainProcessExitRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{186} -} - -func (x *ReportMainProcessExitRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *ReportMainProcessExitRequest) GetInstanceId() string { - if x != nil { - return x.InstanceId - } - return "" -} - -func (x *ReportMainProcessExitRequest) GetExitCode() int32 { - if x != nil { - return x.ExitCode - } - return 0 -} - -type ReportMainProcessExitResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReportMainProcessExitResponse) Reset() { - *x = ReportMainProcessExitResponse{} - mi := &file_openshell_proto_msgTypes[187] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReportMainProcessExitResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportMainProcessExitResponse) ProtoMessage() {} - -func (x *ReportMainProcessExitResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[187] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportMainProcessExitResponse.ProtoReflect.Descriptor instead. -func (*ReportMainProcessExitResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{187} -} - -// Terminal-delivery completion reported after all expected foreground SSH -// attachments close. A successful response permits ephemeral cleanup. -type FinalizeMainProcessExitRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - InstanceId string `protobuf:"bytes,2,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *FinalizeMainProcessExitRequest) Reset() { - *x = FinalizeMainProcessExitRequest{} - mi := &file_openshell_proto_msgTypes[188] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *FinalizeMainProcessExitRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FinalizeMainProcessExitRequest) ProtoMessage() {} - -func (x *FinalizeMainProcessExitRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[188] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FinalizeMainProcessExitRequest.ProtoReflect.Descriptor instead. -func (*FinalizeMainProcessExitRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{188} -} - -func (x *FinalizeMainProcessExitRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *FinalizeMainProcessExitRequest) GetInstanceId() string { - if x != nil { - return x.InstanceId - } - return "" -} - -type FinalizeMainProcessExitResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *FinalizeMainProcessExitResponse) Reset() { - *x = FinalizeMainProcessExitResponse{} - mi := &file_openshell_proto_msgTypes[189] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *FinalizeMainProcessExitResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FinalizeMainProcessExitResponse) ProtoMessage() {} - -func (x *FinalizeMainProcessExitResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[189] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FinalizeMainProcessExitResponse.ProtoReflect.Descriptor instead. -func (*FinalizeMainProcessExitResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{189} -} - -// Gateway requests the supervisor to open a relay channel. -// -// On receiving this, the supervisor should initiate a RelayStream RPC to -// the gateway, sending a RelayInit in the first RelayFrame to associate -// the new HTTP/2 stream with the pending relay slot. The supervisor -// bridges that stream to the requested local target. -type RelayOpen struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Gateway-allocated channel identifier (UUID). - ChannelId string `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` - // Target the supervisor should dial inside the sandbox. - // If absent, supervisors treat the relay as SSH for compatibility. - // - // Types that are valid to be assigned to Target: - // - // *RelayOpen_Ssh - // *RelayOpen_Tcp - Target isRelayOpen_Target `protobuf_oneof:"target"` - // Optional service identifier for audit/correlation. - ServiceId string `protobuf:"bytes,5,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RelayOpen) Reset() { - *x = RelayOpen{} - mi := &file_openshell_proto_msgTypes[190] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RelayOpen) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RelayOpen) ProtoMessage() {} - -func (x *RelayOpen) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[190] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RelayOpen.ProtoReflect.Descriptor instead. -func (*RelayOpen) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{190} -} - -func (x *RelayOpen) GetChannelId() string { - if x != nil { - return x.ChannelId - } - return "" -} - -func (x *RelayOpen) GetTarget() isRelayOpen_Target { - if x != nil { - return x.Target - } - return nil -} - -func (x *RelayOpen) GetSsh() *SshRelayTarget { - if x != nil { - if x, ok := x.Target.(*RelayOpen_Ssh); ok { - return x.Ssh - } - } - return nil -} - -func (x *RelayOpen) GetTcp() *TcpRelayTarget { - if x != nil { - if x, ok := x.Target.(*RelayOpen_Tcp); ok { - return x.Tcp - } - } - return nil -} - -func (x *RelayOpen) GetServiceId() string { - if x != nil { - return x.ServiceId - } - return "" -} - -type isRelayOpen_Target interface { - isRelayOpen_Target() -} - -type RelayOpen_Ssh struct { - Ssh *SshRelayTarget `protobuf:"bytes,2,opt,name=ssh,proto3,oneof"` -} - -type RelayOpen_Tcp struct { - Tcp *TcpRelayTarget `protobuf:"bytes,3,opt,name=tcp,proto3,oneof"` -} - -func (*RelayOpen_Ssh) isRelayOpen_Target() {} - -func (*RelayOpen_Tcp) isRelayOpen_Target() {} - -// Built-in SSH relay target. -type SshRelayTarget struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SshRelayTarget) Reset() { - *x = SshRelayTarget{} - mi := &file_openshell_proto_msgTypes[191] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SshRelayTarget) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SshRelayTarget) ProtoMessage() {} - -func (x *SshRelayTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[191] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SshRelayTarget.ProtoReflect.Descriptor instead. -func (*SshRelayTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{191} -} - -// TCP target dialed by the supervisor from inside the sandbox. -type TcpRelayTarget struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Phase 1 accepts loopback only: 127.0.0.1, ::1, or localhost. - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - // Target port. Must fit in u16 and be non-zero. - Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TcpRelayTarget) Reset() { - *x = TcpRelayTarget{} - mi := &file_openshell_proto_msgTypes[192] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TcpRelayTarget) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TcpRelayTarget) ProtoMessage() {} - -func (x *TcpRelayTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[192] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TcpRelayTarget.ProtoReflect.Descriptor instead. -func (*TcpRelayTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{192} -} - -func (x *TcpRelayTarget) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *TcpRelayTarget) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - -// Initial RelayStream frame sent by the supervisor to claim a pending relay. -type RelayInit struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Gateway-allocated channel identifier (UUID). - ChannelId string `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RelayInit) Reset() { - *x = RelayInit{} - mi := &file_openshell_proto_msgTypes[193] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RelayInit) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RelayInit) ProtoMessage() {} - -func (x *RelayInit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[193] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RelayInit.ProtoReflect.Descriptor instead. -func (*RelayInit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{193} -} - -func (x *RelayInit) GetChannelId() string { - if x != nil { - return x.ChannelId - } - return "" -} - -// A single frame on the RelayStream RPC. -// -// The supervisor MUST send `init` as the first frame. All subsequent frames -// in either direction carry raw bytes in `data`. -type RelayFrame struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *RelayFrame_Init - // *RelayFrame_Data - Payload isRelayFrame_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RelayFrame) Reset() { - *x = RelayFrame{} - mi := &file_openshell_proto_msgTypes[194] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RelayFrame) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RelayFrame) ProtoMessage() {} - -func (x *RelayFrame) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[194] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RelayFrame.ProtoReflect.Descriptor instead. -func (*RelayFrame) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{194} -} - -func (x *RelayFrame) GetPayload() isRelayFrame_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *RelayFrame) GetInit() *RelayInit { - if x != nil { - if x, ok := x.Payload.(*RelayFrame_Init); ok { - return x.Init - } - } - return nil -} - -func (x *RelayFrame) GetData() []byte { - if x != nil { - if x, ok := x.Payload.(*RelayFrame_Data); ok { - return x.Data - } - } - return nil -} - -type isRelayFrame_Payload interface { - isRelayFrame_Payload() -} - -type RelayFrame_Init struct { - Init *RelayInit `protobuf:"bytes,1,opt,name=init,proto3,oneof"` -} - -type RelayFrame_Data struct { - Data []byte `protobuf:"bytes,2,opt,name=data,proto3,oneof"` -} - -func (*RelayFrame_Init) isRelayFrame_Payload() {} - -func (*RelayFrame_Data) isRelayFrame_Payload() {} - -// Initial frame for gateway peer relay forwarding. -type PeerRelayInit struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Stable sandbox UUID whose supervisor relay should be opened. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Relay target to ask the owning gateway to open on its local supervisor - // session. The channel_id is assigned by the forwarding gateway. - RelayOpen *RelayOpen `protobuf:"bytes,2,opt,name=relay_open,json=relayOpen,proto3" json:"relay_open,omitempty"` - // Gateway replica id that initiated the peer relay. - RequesterReplicaId string `protobuf:"bytes,3,opt,name=requester_replica_id,json=requesterReplicaId,proto3" json:"requester_replica_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PeerRelayInit) Reset() { - *x = PeerRelayInit{} - mi := &file_openshell_proto_msgTypes[195] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PeerRelayInit) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PeerRelayInit) ProtoMessage() {} - -func (x *PeerRelayInit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[195] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PeerRelayInit.ProtoReflect.Descriptor instead. -func (*PeerRelayInit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{195} -} - -func (x *PeerRelayInit) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *PeerRelayInit) GetRelayOpen() *RelayOpen { - if x != nil { - return x.RelayOpen - } - return nil -} - -func (x *PeerRelayInit) GetRequesterReplicaId() string { - if x != nil { - return x.RequesterReplicaId - } - return "" -} - -// A single frame on the gateway-to-gateway peer relay RPC. -type PeerRelayFrame struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *PeerRelayFrame_Init - // *PeerRelayFrame_Data - Payload isPeerRelayFrame_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PeerRelayFrame) Reset() { - *x = PeerRelayFrame{} - mi := &file_openshell_proto_msgTypes[196] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PeerRelayFrame) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PeerRelayFrame) ProtoMessage() {} - -func (x *PeerRelayFrame) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[196] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PeerRelayFrame.ProtoReflect.Descriptor instead. -func (*PeerRelayFrame) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{196} -} - -func (x *PeerRelayFrame) GetPayload() isPeerRelayFrame_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *PeerRelayFrame) GetInit() *PeerRelayInit { - if x != nil { - if x, ok := x.Payload.(*PeerRelayFrame_Init); ok { - return x.Init - } - } - return nil -} - -func (x *PeerRelayFrame) GetData() []byte { - if x != nil { - if x, ok := x.Payload.(*PeerRelayFrame_Data); ok { - return x.Data - } - } - return nil -} - -type isPeerRelayFrame_Payload interface { - isPeerRelayFrame_Payload() -} - -type PeerRelayFrame_Init struct { - Init *PeerRelayInit `protobuf:"bytes,1,opt,name=init,proto3,oneof"` -} - -type PeerRelayFrame_Data struct { - Data []byte `protobuf:"bytes,2,opt,name=data,proto3,oneof"` -} - -func (*PeerRelayFrame_Init) isPeerRelayFrame_Payload() {} - -func (*PeerRelayFrame_Data) isPeerRelayFrame_Payload() {} - -type PeerConfigSandboxTarget struct { - state protoimpl.MessageState `protogen:"open.v1"` - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Fences a targeted notification to the session observed by the sending replica. - SessionId string `protobuf:"bytes,2,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PeerConfigSandboxTarget) Reset() { - *x = PeerConfigSandboxTarget{} - mi := &file_openshell_proto_msgTypes[197] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PeerConfigSandboxTarget) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PeerConfigSandboxTarget) ProtoMessage() {} - -func (x *PeerConfigSandboxTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[197] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PeerConfigSandboxTarget.ProtoReflect.Descriptor instead. -func (*PeerConfigSandboxTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{197} -} - -func (x *PeerConfigSandboxTarget) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *PeerConfigSandboxTarget) GetSessionId() string { - if x != nil { - return x.SessionId - } - return "" -} - -// Sandboxes in a workspace that attach the named provider. -type PeerConfigProviderTarget struct { - state protoimpl.MessageState `protogen:"open.v1"` - Workspace string `protobuf:"bytes,1,opt,name=workspace,proto3" json:"workspace,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PeerConfigProviderTarget) Reset() { - *x = PeerConfigProviderTarget{} - mi := &file_openshell_proto_msgTypes[198] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PeerConfigProviderTarget) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PeerConfigProviderTarget) ProtoMessage() {} - -func (x *PeerConfigProviderTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[198] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PeerConfigProviderTarget.ProtoReflect.Descriptor instead. -func (*PeerConfigProviderTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{198} -} - -func (x *PeerConfigProviderTarget) GetWorkspace() string { - if x != nil { - return x.Workspace - } - return "" -} - -func (x *PeerConfigProviderTarget) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type PeerNotifyConfigUpdateRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Scope: - // - // *PeerNotifyConfigUpdateRequest_Sandbox - // *PeerNotifyConfigUpdateRequest_Workspace - // *PeerNotifyConfigUpdateRequest_AllConnected - // *PeerNotifyConfigUpdateRequest_Provider - Scope isPeerNotifyConfigUpdateRequest_Scope `protobuf_oneof:"scope"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PeerNotifyConfigUpdateRequest) Reset() { - *x = PeerNotifyConfigUpdateRequest{} - mi := &file_openshell_proto_msgTypes[199] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PeerNotifyConfigUpdateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PeerNotifyConfigUpdateRequest) ProtoMessage() {} - -func (x *PeerNotifyConfigUpdateRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[199] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PeerNotifyConfigUpdateRequest.ProtoReflect.Descriptor instead. -func (*PeerNotifyConfigUpdateRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{199} -} - -func (x *PeerNotifyConfigUpdateRequest) GetScope() isPeerNotifyConfigUpdateRequest_Scope { - if x != nil { - return x.Scope - } - return nil -} - -func (x *PeerNotifyConfigUpdateRequest) GetSandbox() *PeerConfigSandboxTarget { - if x != nil { - if x, ok := x.Scope.(*PeerNotifyConfigUpdateRequest_Sandbox); ok { - return x.Sandbox - } - } - return nil -} - -func (x *PeerNotifyConfigUpdateRequest) GetWorkspace() string { - if x != nil { - if x, ok := x.Scope.(*PeerNotifyConfigUpdateRequest_Workspace); ok { - return x.Workspace - } - } - return "" -} - -func (x *PeerNotifyConfigUpdateRequest) GetAllConnected() bool { - if x != nil { - if x, ok := x.Scope.(*PeerNotifyConfigUpdateRequest_AllConnected); ok { - return x.AllConnected - } - } - return false -} - -func (x *PeerNotifyConfigUpdateRequest) GetProvider() *PeerConfigProviderTarget { - if x != nil { - if x, ok := x.Scope.(*PeerNotifyConfigUpdateRequest_Provider); ok { - return x.Provider - } - } - return nil -} - -type isPeerNotifyConfigUpdateRequest_Scope interface { - isPeerNotifyConfigUpdateRequest_Scope() -} - -type PeerNotifyConfigUpdateRequest_Sandbox struct { - Sandbox *PeerConfigSandboxTarget `protobuf:"bytes,1,opt,name=sandbox,proto3,oneof"` -} - -type PeerNotifyConfigUpdateRequest_Workspace struct { - Workspace string `protobuf:"bytes,2,opt,name=workspace,proto3,oneof"` -} - -type PeerNotifyConfigUpdateRequest_AllConnected struct { - AllConnected bool `protobuf:"varint,3,opt,name=all_connected,json=allConnected,proto3,oneof"` -} - -type PeerNotifyConfigUpdateRequest_Provider struct { - Provider *PeerConfigProviderTarget `protobuf:"bytes,4,opt,name=provider,proto3,oneof"` -} - -func (*PeerNotifyConfigUpdateRequest_Sandbox) isPeerNotifyConfigUpdateRequest_Scope() {} - -func (*PeerNotifyConfigUpdateRequest_Workspace) isPeerNotifyConfigUpdateRequest_Scope() {} - -func (*PeerNotifyConfigUpdateRequest_AllConnected) isPeerNotifyConfigUpdateRequest_Scope() {} - -func (*PeerNotifyConfigUpdateRequest_Provider) isPeerNotifyConfigUpdateRequest_Scope() {} - -type PeerNotifyConfigUpdateResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The targeted session moved before the receiving replica accepted the notification. - StaleOwner bool `protobuf:"varint,1,opt,name=stale_owner,json=staleOwner,proto3" json:"stale_owner,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PeerNotifyConfigUpdateResponse) Reset() { - *x = PeerNotifyConfigUpdateResponse{} - mi := &file_openshell_proto_msgTypes[200] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PeerNotifyConfigUpdateResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PeerNotifyConfigUpdateResponse) ProtoMessage() {} - -func (x *PeerNotifyConfigUpdateResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[200] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PeerNotifyConfigUpdateResponse.ProtoReflect.Descriptor instead. -func (*PeerNotifyConfigUpdateResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{200} -} - -func (x *PeerNotifyConfigUpdateResponse) GetStaleOwner() bool { - if x != nil { - return x.StaleOwner - } - return false -} - -// Supervisor reports the result of a relay open request. -type RelayOpenResult struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Channel identifier from the RelayOpen request. - ChannelId string `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` - // True if the relay was successfully established. - Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` - // Error message if success is false. - Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RelayOpenResult) Reset() { - *x = RelayOpenResult{} - mi := &file_openshell_proto_msgTypes[201] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RelayOpenResult) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RelayOpenResult) ProtoMessage() {} - -func (x *RelayOpenResult) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[201] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RelayOpenResult.ProtoReflect.Descriptor instead. -func (*RelayOpenResult) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{201} -} - -func (x *RelayOpenResult) GetChannelId() string { - if x != nil { - return x.ChannelId - } - return "" -} - -func (x *RelayOpenResult) GetSuccess() bool { - if x != nil { - return x.Success - } - return false -} - -func (x *RelayOpenResult) GetError() string { - if x != nil { - return x.Error - } - return "" -} - -// Either side requests closure of a relay channel. -type RelayClose struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Channel identifier to close. - ChannelId string `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` - // Optional reason for closure. - Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RelayClose) Reset() { - *x = RelayClose{} - mi := &file_openshell_proto_msgTypes[202] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RelayClose) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RelayClose) ProtoMessage() {} - -func (x *RelayClose) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[202] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RelayClose.ProtoReflect.Descriptor instead. -func (*RelayClose) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{202} -} - -func (x *RelayClose) GetChannelId() string { - if x != nil { - return x.ChannelId - } - return "" -} - -func (x *RelayClose) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -// Observed HTTP method+path pattern from L7 inspection. -type L7RequestSample struct { - state protoimpl.MessageState `protogen:"open.v1"` - // HTTP method: GET, POST, PUT, DELETE, etc. - Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` - // HTTP path: /v1/models, /repos/myorg/issues - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` - // L7 decision: "audit" or "deny" (allowed requests not collected). - Decision string `protobuf:"bytes,3,opt,name=decision,proto3" json:"decision,omitempty"` - // Number of times this (method, path) was observed. - Count uint32 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *L7RequestSample) Reset() { - *x = L7RequestSample{} - mi := &file_openshell_proto_msgTypes[203] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *L7RequestSample) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L7RequestSample) ProtoMessage() {} - -func (x *L7RequestSample) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[203] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L7RequestSample.ProtoReflect.Descriptor instead. -func (*L7RequestSample) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{203} -} - -func (x *L7RequestSample) GetMethod() string { - if x != nil { - return x.Method - } - return "" -} - -func (x *L7RequestSample) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *L7RequestSample) GetDecision() string { - if x != nil { - return x.Decision - } - return "" -} - -func (x *L7RequestSample) GetCount() uint32 { - if x != nil { - return x.Count - } - return 0 -} - -// Structured denial summary from sandbox aggregator. -type DenialSummary struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox ID that produced this summary. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Denied destination host. - Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"` - // Denied destination port. - Port uint32 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"` - // Binary that attempted the connection. - Binary string `protobuf:"bytes,4,opt,name=binary,proto3" json:"binary,omitempty"` - // Process ancestor chain. - Ancestors []string `protobuf:"bytes,5,rep,name=ancestors,proto3" json:"ancestors,omitempty"` - // Denial reason from OPA evaluation. - DenyReason string `protobuf:"bytes,6,opt,name=deny_reason,json=denyReason,proto3" json:"deny_reason,omitempty"` - // Time of the first denial. - FirstSeenTime *timestamppb.Timestamp `protobuf:"bytes,107,opt,name=first_seen_time,json=firstSeenTime,proto3" json:"first_seen_time,omitempty"` - // Time of the most recent denial. - LastSeenTime *timestamppb.Timestamp `protobuf:"bytes,108,opt,name=last_seen_time,json=lastSeenTime,proto3" json:"last_seen_time,omitempty"` - // Number of denials in the current window. - Count uint32 `protobuf:"varint,9,opt,name=count,proto3" json:"count,omitempty"` - // Events dropped during aggregator cooldown. - SuppressedCount uint32 `protobuf:"varint,10,opt,name=suppressed_count,json=suppressedCount,proto3" json:"suppressed_count,omitempty"` - // Cumulative lifetime count (never resets). - TotalCount uint32 `protobuf:"varint,11,opt,name=total_count,json=totalCount,proto3" json:"total_count,omitempty"` - // Distinct cmdline strings observed (sanitized of credentials). - SampleCmdlines []string `protobuf:"bytes,12,rep,name=sample_cmdlines,json=sampleCmdlines,proto3" json:"sample_cmdlines,omitempty"` - // SHA-256 of the binary for audit trail. - BinarySha256 string `protobuf:"bytes,13,opt,name=binary_sha256,json=binarySha256,proto3" json:"binary_sha256,omitempty"` - // True if emitted by stale-flush rather than threshold. - Persistent bool `protobuf:"varint,14,opt,name=persistent,proto3" json:"persistent,omitempty"` - // Denial category: "l4_deny", "l7_deny", "l7_audit", "ssrf". - DenialStage string `protobuf:"bytes,15,opt,name=denial_stage,json=denialStage,proto3" json:"denial_stage,omitempty"` - // Observed HTTP request patterns (from L7 inspection). - L7RequestSamples []*L7RequestSample `protobuf:"bytes,16,rep,name=l7_request_samples,json=l7RequestSamples,proto3" json:"l7_request_samples,omitempty"` - // True if L7 inspection was active during observation window. - L7InspectionActive bool `protobuf:"varint,17,opt,name=l7_inspection_active,json=l7InspectionActive,proto3" json:"l7_inspection_active,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DenialSummary) Reset() { - *x = DenialSummary{} - mi := &file_openshell_proto_msgTypes[204] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DenialSummary) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DenialSummary) ProtoMessage() {} - -func (x *DenialSummary) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[204] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DenialSummary.ProtoReflect.Descriptor instead. -func (*DenialSummary) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{204} -} - -func (x *DenialSummary) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *DenialSummary) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *DenialSummary) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - -func (x *DenialSummary) GetBinary() string { - if x != nil { - return x.Binary - } - return "" -} - -func (x *DenialSummary) GetAncestors() []string { - if x != nil { - return x.Ancestors - } - return nil -} - -func (x *DenialSummary) GetDenyReason() string { - if x != nil { - return x.DenyReason - } - return "" -} - -func (x *DenialSummary) GetFirstSeenTime() *timestamppb.Timestamp { - if x != nil { - return x.FirstSeenTime - } - return nil -} - -func (x *DenialSummary) GetLastSeenTime() *timestamppb.Timestamp { - if x != nil { - return x.LastSeenTime - } - return nil -} - -func (x *DenialSummary) GetCount() uint32 { - if x != nil { - return x.Count - } - return 0 -} - -func (x *DenialSummary) GetSuppressedCount() uint32 { - if x != nil { - return x.SuppressedCount - } - return 0 -} - -func (x *DenialSummary) GetTotalCount() uint32 { - if x != nil { - return x.TotalCount - } - return 0 -} - -func (x *DenialSummary) GetSampleCmdlines() []string { - if x != nil { - return x.SampleCmdlines - } - return nil -} - -func (x *DenialSummary) GetBinarySha256() string { - if x != nil { - return x.BinarySha256 - } - return "" -} - -func (x *DenialSummary) GetPersistent() bool { - if x != nil { - return x.Persistent - } - return false -} - -func (x *DenialSummary) GetDenialStage() string { - if x != nil { - return x.DenialStage - } - return "" -} - -func (x *DenialSummary) GetL7RequestSamples() []*L7RequestSample { - if x != nil { - return x.L7RequestSamples - } - return nil -} - -func (x *DenialSummary) GetL7InspectionActive() bool { - if x != nil { - return x.L7InspectionActive - } - return false -} - -// Count of denied actions grouped only by sanitized telemetry category. -type DenialGroupCount struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sanitized denial category, e.g. "connect_policy", "l7_policy", "ssrf". - DenyGroup string `protobuf:"bytes,1,opt,name=deny_group,json=denyGroup,proto3" json:"deny_group,omitempty"` - // Number of denied actions in this category. - DeniedCount uint32 `protobuf:"varint,2,opt,name=denied_count,json=deniedCount,proto3" json:"denied_count,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DenialGroupCount) Reset() { - *x = DenialGroupCount{} - mi := &file_openshell_proto_msgTypes[205] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DenialGroupCount) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DenialGroupCount) ProtoMessage() {} - -func (x *DenialGroupCount) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[205] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DenialGroupCount.ProtoReflect.Descriptor instead. -func (*DenialGroupCount) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{205} -} - -func (x *DenialGroupCount) GetDenyGroup() string { - if x != nil { - return x.DenyGroup - } - return "" -} - -func (x *DenialGroupCount) GetDeniedCount() uint32 { - if x != nil { - return x.DeniedCount - } - return 0 -} - -// Anonymous sandbox network activity counters. This intentionally excludes -// hosts, paths, binaries, raw deny reasons, sandbox IDs, and user content. -type NetworkActivitySummary struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Total observed network activities in the current window. - NetworkActivityCount uint32 `protobuf:"varint,1,opt,name=network_activity_count,json=networkActivityCount,proto3" json:"network_activity_count,omitempty"` - // Total denied actions in the current window. - DeniedActionCount uint32 `protobuf:"varint,2,opt,name=denied_action_count,json=deniedActionCount,proto3" json:"denied_action_count,omitempty"` - // Denied action counts grouped by sanitized category. - DenialsByGroup []*DenialGroupCount `protobuf:"bytes,3,rep,name=denials_by_group,json=denialsByGroup,proto3" json:"denials_by_group,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *NetworkActivitySummary) Reset() { - *x = NetworkActivitySummary{} - mi := &file_openshell_proto_msgTypes[206] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *NetworkActivitySummary) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NetworkActivitySummary) ProtoMessage() {} - -func (x *NetworkActivitySummary) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[206] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NetworkActivitySummary.ProtoReflect.Descriptor instead. -func (*NetworkActivitySummary) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{206} -} - -func (x *NetworkActivitySummary) GetNetworkActivityCount() uint32 { - if x != nil { - return x.NetworkActivityCount - } - return 0 -} - -func (x *NetworkActivitySummary) GetDeniedActionCount() uint32 { - if x != nil { - return x.DeniedActionCount - } - return 0 -} - -func (x *NetworkActivitySummary) GetDenialsByGroup() []*DenialGroupCount { - if x != nil { - return x.DenialsByGroup - } - return nil -} - -// A proposed policy rule with rationale and approval status. -type PolicyChunk struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Unique chunk identifier. - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - // Approval status: "pending", "approved", "rejected". - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - // Proposed network_policies map key. - RuleName string `protobuf:"bytes,3,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - // The proposed network policy rule. - ProposedRule *sandboxv1.NetworkPolicyRule `protobuf:"bytes,4,opt,name=proposed_rule,json=proposedRule,proto3" json:"proposed_rule,omitempty"` - // Human-readable explanation of why this rule is proposed. - Rationale string `protobuf:"bytes,5,opt,name=rationale,proto3" json:"rationale,omitempty"` - // Security concerns flagged by analysis (empty if none). - SecurityNotes string `protobuf:"bytes,6,opt,name=security_notes,json=securityNotes,proto3" json:"security_notes,omitempty"` - // Analysis confidence (0.0-1.0). 0 for mechanistic mode. - Confidence float32 `protobuf:"fixed32,7,opt,name=confidence,proto3" json:"confidence,omitempty"` - // IDs of denial summaries that led to this chunk. - DenialSummaryIds []string `protobuf:"bytes,8,rep,name=denial_summary_ids,json=denialSummaryIds,proto3" json:"denial_summary_ids,omitempty"` - // Time when this chunk was created. - CreatedTime *timestamppb.Timestamp `protobuf:"bytes,109,opt,name=created_time,json=createdTime,proto3" json:"created_time,omitempty"` - // Time when the user approved or rejected the chunk. Absent if undecided. - DecidedTime *timestamppb.Timestamp `protobuf:"bytes,110,opt,name=decided_time,json=decidedTime,proto3" json:"decided_time,omitempty"` - // Recommendation stage: "initial" or "refined" (progressive L7 visibility). - Stage string `protobuf:"bytes,11,opt,name=stage,proto3" json:"stage,omitempty"` - // For stage="refined": the initial chunk this replaces. - SupersedesChunkId string `protobuf:"bytes,12,opt,name=supersedes_chunk_id,json=supersedesChunkId,proto3" json:"supersedes_chunk_id,omitempty"` - // How many times this endpoint has been seen across denial flush cycles. - HitCount int32 `protobuf:"varint,13,opt,name=hit_count,json=hitCount,proto3" json:"hit_count,omitempty"` - // First time this endpoint was proposed. - FirstSeenTime *timestamppb.Timestamp `protobuf:"bytes,114,opt,name=first_seen_time,json=firstSeenTime,proto3" json:"first_seen_time,omitempty"` - // Most recent time this endpoint was proposed again. - LastSeenTime *timestamppb.Timestamp `protobuf:"bytes,115,opt,name=last_seen_time,json=lastSeenTime,proto3" json:"last_seen_time,omitempty"` - // Binary path that triggered the denial (denormalized for display convenience). - Binary string `protobuf:"bytes,16,opt,name=binary,proto3" json:"binary,omitempty"` - // Validation verdict from gateway-side static checks (prover output). - // Free-form summary string for human consumption in the inbox card. - // Empty until the prover has run for this chunk. - ValidationResult string `protobuf:"bytes,17,opt,name=validation_result,json=validationResult,proto3" json:"validation_result,omitempty"` - // Operator-supplied free-form text accompanying a rejection. Populated - // when the reviewer rejects via `RejectDraftChunkRequest.reason`; surfaced - // back to the in-sandbox agent so it can revise the proposal. - // Empty for non-rejected chunks. - RejectionReason string `protobuf:"bytes,18,opt,name=rejection_reason,json=rejectionReason,proto3" json:"rejection_reason,omitempty"` - // Gateway-side merge/application preflight failure. Kept separate from - // prover output and operator rejection so clients can explain why a - // prover-clean proposal is not currently applicable. - ApplicationError string `protobuf:"bytes,19,opt,name=application_error,json=applicationError,proto3" json:"application_error,omitempty"` - // Opaque digest binding review to the exact live inputs and complete - // effective candidate evaluated by the gateway. - ReviewToken string `protobuf:"bytes,20,opt,name=review_token,json=reviewToken,proto3" json:"review_token,omitempty"` - // Deterministic hashes for compact candidate display and diagnostics. - CurrentEffectivePolicyHash string `protobuf:"bytes,21,opt,name=current_effective_policy_hash,json=currentEffectivePolicyHash,proto3" json:"current_effective_policy_hash,omitempty"` - CandidateEffectivePolicyHash string `protobuf:"bytes,22,opt,name=candidate_effective_policy_hash,json=candidateEffectivePolicyHash,proto3" json:"candidate_effective_policy_hash,omitempty"` - // Complete effective policies used for review. These contain policy - // configuration only; credential secret values are never materialized. - CurrentEffectivePolicy *sandboxv1.SandboxPolicy `protobuf:"bytes,23,opt,name=current_effective_policy,json=currentEffectivePolicy,proto3" json:"current_effective_policy,omitempty"` - CandidateEffectivePolicy *sandboxv1.SandboxPolicy `protobuf:"bytes,24,opt,name=candidate_effective_policy,json=candidateEffectivePolicy,proto3" json:"candidate_effective_policy,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PolicyChunk) Reset() { - *x = PolicyChunk{} - mi := &file_openshell_proto_msgTypes[207] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PolicyChunk) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PolicyChunk) ProtoMessage() {} - -func (x *PolicyChunk) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[207] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PolicyChunk.ProtoReflect.Descriptor instead. -func (*PolicyChunk) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{207} -} - -func (x *PolicyChunk) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *PolicyChunk) GetStatus() string { - if x != nil { - return x.Status - } - return "" -} - -func (x *PolicyChunk) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -func (x *PolicyChunk) GetProposedRule() *sandboxv1.NetworkPolicyRule { - if x != nil { - return x.ProposedRule - } - return nil -} - -func (x *PolicyChunk) GetRationale() string { - if x != nil { - return x.Rationale - } - return "" -} - -func (x *PolicyChunk) GetSecurityNotes() string { - if x != nil { - return x.SecurityNotes - } - return "" -} - -func (x *PolicyChunk) GetConfidence() float32 { - if x != nil { - return x.Confidence - } - return 0 -} - -func (x *PolicyChunk) GetDenialSummaryIds() []string { - if x != nil { - return x.DenialSummaryIds - } - return nil -} - -func (x *PolicyChunk) GetCreatedTime() *timestamppb.Timestamp { - if x != nil { - return x.CreatedTime - } - return nil -} - -func (x *PolicyChunk) GetDecidedTime() *timestamppb.Timestamp { - if x != nil { - return x.DecidedTime - } - return nil -} - -func (x *PolicyChunk) GetStage() string { - if x != nil { - return x.Stage - } - return "" -} - -func (x *PolicyChunk) GetSupersedesChunkId() string { - if x != nil { - return x.SupersedesChunkId - } - return "" -} - -func (x *PolicyChunk) GetHitCount() int32 { - if x != nil { - return x.HitCount - } - return 0 -} - -func (x *PolicyChunk) GetFirstSeenTime() *timestamppb.Timestamp { - if x != nil { - return x.FirstSeenTime - } - return nil -} - -func (x *PolicyChunk) GetLastSeenTime() *timestamppb.Timestamp { - if x != nil { - return x.LastSeenTime - } - return nil -} - -func (x *PolicyChunk) GetBinary() string { - if x != nil { - return x.Binary - } - return "" -} - -func (x *PolicyChunk) GetValidationResult() string { - if x != nil { - return x.ValidationResult - } - return "" -} - -func (x *PolicyChunk) GetRejectionReason() string { - if x != nil { - return x.RejectionReason - } - return "" -} - -func (x *PolicyChunk) GetApplicationError() string { - if x != nil { - return x.ApplicationError - } - return "" -} - -func (x *PolicyChunk) GetReviewToken() string { - if x != nil { - return x.ReviewToken - } - return "" -} - -func (x *PolicyChunk) GetCurrentEffectivePolicyHash() string { - if x != nil { - return x.CurrentEffectivePolicyHash - } - return "" -} - -func (x *PolicyChunk) GetCandidateEffectivePolicyHash() string { - if x != nil { - return x.CandidateEffectivePolicyHash - } - return "" -} - -func (x *PolicyChunk) GetCurrentEffectivePolicy() *sandboxv1.SandboxPolicy { - if x != nil { - return x.CurrentEffectivePolicy - } - return nil -} - -func (x *PolicyChunk) GetCandidateEffectivePolicy() *sandboxv1.SandboxPolicy { - if x != nil { - return x.CandidateEffectivePolicy - } - return nil -} - -// Notification that the draft policy was updated. -type DraftPolicyUpdate struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Current draft version. - DraftVersion uint64 `protobuf:"varint,1,opt,name=draft_version,json=draftVersion,proto3" json:"draft_version,omitempty"` - // Number of new chunks added in this update. - NewChunks uint32 `protobuf:"varint,2,opt,name=new_chunks,json=newChunks,proto3" json:"new_chunks,omitempty"` - // Total pending chunks awaiting approval. - TotalPending uint32 `protobuf:"varint,3,opt,name=total_pending,json=totalPending,proto3" json:"total_pending,omitempty"` - // Brief description of what changed. - Summary string `protobuf:"bytes,4,opt,name=summary,proto3" json:"summary,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DraftPolicyUpdate) Reset() { - *x = DraftPolicyUpdate{} - mi := &file_openshell_proto_msgTypes[208] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DraftPolicyUpdate) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DraftPolicyUpdate) ProtoMessage() {} - -func (x *DraftPolicyUpdate) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[208] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DraftPolicyUpdate.ProtoReflect.Descriptor instead. -func (*DraftPolicyUpdate) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{208} -} - -func (x *DraftPolicyUpdate) GetDraftVersion() uint64 { - if x != nil { - return x.DraftVersion - } - return 0 -} - -func (x *DraftPolicyUpdate) GetNewChunks() uint32 { - if x != nil { - return x.NewChunks - } - return 0 -} - -func (x *DraftPolicyUpdate) GetTotalPending() uint32 { - if x != nil { - return x.TotalPending - } - return 0 -} - -func (x *DraftPolicyUpdate) GetSummary() string { - if x != nil { - return x.Summary - } - return "" -} - -// Submit analysis results from sandbox to gateway. -type SubmitPolicyAnalysisRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,6,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Aggregated denial summaries. - Summaries []*DenialSummary `protobuf:"bytes,1,rep,name=summaries,proto3" json:"summaries,omitempty"` - // Proposed policy chunks (validated by sandbox OPA engine). - ProposedChunks []*PolicyChunk `protobuf:"bytes,2,rep,name=proposed_chunks,json=proposedChunks,proto3" json:"proposed_chunks,omitempty"` - // Analysis mode. `mechanistic` is the observation-driven path from the - // denial aggregator — chunks targeting the same host|port|binary fold - // into one row with hit_count incremented. `agent_authored` is an - // intentional proposal from an in-sandbox agent — each submission lands - // as its own chunk so the redraft-after-rejection loop has a stable id - // to watch. Other values are treated as agent-style (no dedup) so a new - // mode does not silently collapse proposals. - AnalysisMode string `protobuf:"bytes,3,opt,name=analysis_mode,json=analysisMode,proto3" json:"analysis_mode,omitempty"` - // Sandbox name. The authenticated sandbox principal remains authoritative - // for this internal callback. - Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` - // Anonymous network activity counters. - NetworkActivitySummaries []*NetworkActivitySummary `protobuf:"bytes,5,rep,name=network_activity_summaries,json=networkActivitySummaries,proto3" json:"network_activity_summaries,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SubmitPolicyAnalysisRequest) Reset() { - *x = SubmitPolicyAnalysisRequest{} - mi := &file_openshell_proto_msgTypes[209] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SubmitPolicyAnalysisRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubmitPolicyAnalysisRequest) ProtoMessage() {} - -func (x *SubmitPolicyAnalysisRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[209] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SubmitPolicyAnalysisRequest.ProtoReflect.Descriptor instead. -func (*SubmitPolicyAnalysisRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{209} -} - -func (x *SubmitPolicyAnalysisRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *SubmitPolicyAnalysisRequest) GetSummaries() []*DenialSummary { - if x != nil { - return x.Summaries - } - return nil -} - -func (x *SubmitPolicyAnalysisRequest) GetProposedChunks() []*PolicyChunk { - if x != nil { - return x.ProposedChunks - } - return nil -} - -func (x *SubmitPolicyAnalysisRequest) GetAnalysisMode() string { - if x != nil { - return x.AnalysisMode - } - return "" -} - -func (x *SubmitPolicyAnalysisRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *SubmitPolicyAnalysisRequest) GetNetworkActivitySummaries() []*NetworkActivitySummary { - if x != nil { - return x.NetworkActivitySummaries - } - return nil -} - -type SubmitPolicyAnalysisResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Number of chunks accepted by the gateway. - AcceptedChunks uint32 `protobuf:"varint,1,opt,name=accepted_chunks,json=acceptedChunks,proto3" json:"accepted_chunks,omitempty"` - // Number of chunks rejected by gateway validation. - RejectedChunks uint32 `protobuf:"varint,2,opt,name=rejected_chunks,json=rejectedChunks,proto3" json:"rejected_chunks,omitempty"` - // Reasons for each rejected chunk. - RejectionReasons []string `protobuf:"bytes,3,rep,name=rejection_reasons,json=rejectionReasons,proto3" json:"rejection_reasons,omitempty"` - // Server-assigned chunk IDs for the accepted chunks, in submission order. - // Agents use these to watch proposal state via policy.local's - // GET /v1/proposals/{id} and /wait endpoints. - AcceptedChunkIds []string `protobuf:"bytes,4,rep,name=accepted_chunk_ids,json=acceptedChunkIds,proto3" json:"accepted_chunk_ids,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SubmitPolicyAnalysisResponse) Reset() { - *x = SubmitPolicyAnalysisResponse{} - mi := &file_openshell_proto_msgTypes[210] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SubmitPolicyAnalysisResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubmitPolicyAnalysisResponse) ProtoMessage() {} - -func (x *SubmitPolicyAnalysisResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[210] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SubmitPolicyAnalysisResponse.ProtoReflect.Descriptor instead. -func (*SubmitPolicyAnalysisResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{210} -} - -func (x *SubmitPolicyAnalysisResponse) GetAcceptedChunks() uint32 { - if x != nil { - return x.AcceptedChunks - } - return 0 -} - -func (x *SubmitPolicyAnalysisResponse) GetRejectedChunks() uint32 { - if x != nil { - return x.RejectedChunks - } - return 0 -} - -func (x *SubmitPolicyAnalysisResponse) GetRejectionReasons() []string { - if x != nil { - return x.RejectionReasons - } - return nil -} - -func (x *SubmitPolicyAnalysisResponse) GetAcceptedChunkIds() []string { - if x != nil { - return x.AcceptedChunkIds - } - return nil -} - -// Get draft policy for a sandbox. -type GetDraftPolicyRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Optional status filter: "pending", "approved", "rejected", or "" for all. - StatusFilter string `protobuf:"bytes,2,opt,name=status_filter,json=statusFilter,proto3" json:"status_filter,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetDraftPolicyRequest) Reset() { - *x = GetDraftPolicyRequest{} - mi := &file_openshell_proto_msgTypes[211] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetDraftPolicyRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetDraftPolicyRequest) ProtoMessage() {} - -func (x *GetDraftPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[211] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetDraftPolicyRequest.ProtoReflect.Descriptor instead. -func (*GetDraftPolicyRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{211} -} - -func (x *GetDraftPolicyRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetDraftPolicyRequest) GetStatusFilter() string { - if x != nil { - return x.StatusFilter - } - return "" -} - -func (x *GetDraftPolicyRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -type GetDraftPolicyResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Draft policy chunks. - Chunks []*PolicyChunk `protobuf:"bytes,1,rep,name=chunks,proto3" json:"chunks,omitempty"` - // LLM-generated summary of all analysis (empty in mechanistic mode). - RollingSummary string `protobuf:"bytes,2,opt,name=rolling_summary,json=rollingSummary,proto3" json:"rolling_summary,omitempty"` - // Current draft version. - DraftVersion uint64 `protobuf:"varint,3,opt,name=draft_version,json=draftVersion,proto3" json:"draft_version,omitempty"` - // Time when the last analysis completed. - LastAnalyzedTime *timestamppb.Timestamp `protobuf:"bytes,104,opt,name=last_analyzed_time,json=lastAnalyzedTime,proto3" json:"last_analyzed_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetDraftPolicyResponse) Reset() { - *x = GetDraftPolicyResponse{} - mi := &file_openshell_proto_msgTypes[212] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetDraftPolicyResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetDraftPolicyResponse) ProtoMessage() {} - -func (x *GetDraftPolicyResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[212] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetDraftPolicyResponse.ProtoReflect.Descriptor instead. -func (*GetDraftPolicyResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{212} -} - -func (x *GetDraftPolicyResponse) GetChunks() []*PolicyChunk { - if x != nil { - return x.Chunks - } - return nil -} - -func (x *GetDraftPolicyResponse) GetRollingSummary() string { - if x != nil { - return x.RollingSummary - } - return "" -} - -func (x *GetDraftPolicyResponse) GetDraftVersion() uint64 { - if x != nil { - return x.DraftVersion - } - return 0 -} - -func (x *GetDraftPolicyResponse) GetLastAnalyzedTime() *timestamppb.Timestamp { - if x != nil { - return x.LastAnalyzedTime - } - return nil -} - -// Approve a single draft chunk. -type ApproveDraftChunkRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Chunk ID to approve. - ChunkId string `protobuf:"bytes,2,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` - // Token returned with the reviewed PolicyChunk. Approval fails with - // FAILED_PRECONDITION if live decision inputs no longer match it. - ReviewToken string `protobuf:"bytes,4,opt,name=review_token,json=reviewToken,proto3" json:"review_token,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ApproveDraftChunkRequest) Reset() { - *x = ApproveDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[213] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ApproveDraftChunkRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ApproveDraftChunkRequest) ProtoMessage() {} - -func (x *ApproveDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[213] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ApproveDraftChunkRequest.ProtoReflect.Descriptor instead. -func (*ApproveDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{213} -} - -func (x *ApproveDraftChunkRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ApproveDraftChunkRequest) GetChunkId() string { - if x != nil { - return x.ChunkId - } - return "" -} - -func (x *ApproveDraftChunkRequest) GetReviewToken() string { - if x != nil { - return x.ReviewToken - } - return "" -} - -func (x *ApproveDraftChunkRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *ApproveDraftChunkRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -type ApproveDraftChunkResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // New policy version after merge. - PolicyVersion uint32 `protobuf:"varint,1,opt,name=policy_version,json=policyVersion,proto3" json:"policy_version,omitempty"` - // SHA-256 hash of the new policy. - PolicyHash string `protobuf:"bytes,2,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ApproveDraftChunkResponse) Reset() { - *x = ApproveDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[214] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ApproveDraftChunkResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ApproveDraftChunkResponse) ProtoMessage() {} - -func (x *ApproveDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[214] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ApproveDraftChunkResponse.ProtoReflect.Descriptor instead. -func (*ApproveDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{214} -} - -func (x *ApproveDraftChunkResponse) GetPolicyVersion() uint32 { - if x != nil { - return x.PolicyVersion - } - return 0 -} - -func (x *ApproveDraftChunkResponse) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -// Reject a single draft chunk. -type RejectDraftChunkRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,4,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Chunk ID to reject. - ChunkId string `protobuf:"bytes,2,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` - // Optional reason for rejection (fed to LLM context in future analysis). - Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RejectDraftChunkRequest) Reset() { - *x = RejectDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[215] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RejectDraftChunkRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RejectDraftChunkRequest) ProtoMessage() {} - -func (x *RejectDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[215] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RejectDraftChunkRequest.ProtoReflect.Descriptor instead. -func (*RejectDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{215} -} - -func (x *RejectDraftChunkRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *RejectDraftChunkRequest) GetChunkId() string { - if x != nil { - return x.ChunkId - } - return "" -} - -func (x *RejectDraftChunkRequest) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -func (x *RejectDraftChunkRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *RejectDraftChunkRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -type RejectDraftChunkResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RejectDraftChunkResponse) Reset() { - *x = RejectDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[216] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RejectDraftChunkResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RejectDraftChunkResponse) ProtoMessage() {} - -func (x *RejectDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[216] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RejectDraftChunkResponse.ProtoReflect.Descriptor instead. -func (*RejectDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{216} -} - -// Approve all pending chunks. -type DraftChunkApproval struct { - state protoimpl.MessageState `protogen:"open.v1"` - ChunkId string `protobuf:"bytes,1,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` - ReviewToken string `protobuf:"bytes,2,opt,name=review_token,json=reviewToken,proto3" json:"review_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DraftChunkApproval) Reset() { - *x = DraftChunkApproval{} - mi := &file_openshell_proto_msgTypes[217] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DraftChunkApproval) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DraftChunkApproval) ProtoMessage() {} - -func (x *DraftChunkApproval) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[217] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DraftChunkApproval.ProtoReflect.Descriptor instead. -func (*DraftChunkApproval) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{217} -} - -func (x *DraftChunkApproval) GetChunkId() string { - if x != nil { - return x.ChunkId - } - return "" -} - -func (x *DraftChunkApproval) GetReviewToken() string { - if x != nil { - return x.ReviewToken - } - return "" -} - -type ApproveAllDraftChunksRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,4,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Include chunks with security_notes (default false: skips them). - IncludeSecurityFlagged bool `protobuf:"varint,2,opt,name=include_security_flagged,json=includeSecurityFlagged,proto3" json:"include_security_flagged,omitempty"` - // Exact reviewed chunks and tokens. The server validates them against one - // live snapshot, stages compatible operations in order, and writes once. - Approvals []*DraftChunkApproval `protobuf:"bytes,3,rep,name=approvals,proto3" json:"approvals,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ApproveAllDraftChunksRequest) Reset() { - *x = ApproveAllDraftChunksRequest{} - mi := &file_openshell_proto_msgTypes[218] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ApproveAllDraftChunksRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ApproveAllDraftChunksRequest) ProtoMessage() {} - -func (x *ApproveAllDraftChunksRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[218] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ApproveAllDraftChunksRequest.ProtoReflect.Descriptor instead. -func (*ApproveAllDraftChunksRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{218} -} - -func (x *ApproveAllDraftChunksRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ApproveAllDraftChunksRequest) GetIncludeSecurityFlagged() bool { - if x != nil { - return x.IncludeSecurityFlagged - } - return false -} - -func (x *ApproveAllDraftChunksRequest) GetApprovals() []*DraftChunkApproval { - if x != nil { - return x.Approvals - } - return nil -} - -func (x *ApproveAllDraftChunksRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *ApproveAllDraftChunksRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -type ApproveAllDraftChunksResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // New policy version after merge. - PolicyVersion uint32 `protobuf:"varint,1,opt,name=policy_version,json=policyVersion,proto3" json:"policy_version,omitempty"` - // SHA-256 hash of the new policy. - PolicyHash string `protobuf:"bytes,2,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - // Number of chunks approved. - ChunksApproved uint32 `protobuf:"varint,3,opt,name=chunks_approved,json=chunksApproved,proto3" json:"chunks_approved,omitempty"` - // Number of chunks skipped for any reason, including security flags, - // stale or invalid candidates, and conflicts within the staged batch. - ChunksSkipped uint32 `protobuf:"varint,4,opt,name=chunks_skipped,json=chunksSkipped,proto3" json:"chunks_skipped,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ApproveAllDraftChunksResponse) Reset() { - *x = ApproveAllDraftChunksResponse{} - mi := &file_openshell_proto_msgTypes[219] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ApproveAllDraftChunksResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ApproveAllDraftChunksResponse) ProtoMessage() {} - -func (x *ApproveAllDraftChunksResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[219] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ApproveAllDraftChunksResponse.ProtoReflect.Descriptor instead. -func (*ApproveAllDraftChunksResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{219} -} - -func (x *ApproveAllDraftChunksResponse) GetPolicyVersion() uint32 { - if x != nil { - return x.PolicyVersion - } - return 0 -} - -func (x *ApproveAllDraftChunksResponse) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *ApproveAllDraftChunksResponse) GetChunksApproved() uint32 { - if x != nil { - return x.ChunksApproved - } - return 0 -} - -func (x *ApproveAllDraftChunksResponse) GetChunksSkipped() uint32 { - if x != nil { - return x.ChunksSkipped - } - return 0 -} - -// Edit a pending chunk in-place. -type EditDraftChunkRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,4,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Chunk ID to edit. - ChunkId string `protobuf:"bytes,2,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` - // The modified rule (replaces existing proposed_rule). - ProposedRule *sandboxv1.NetworkPolicyRule `protobuf:"bytes,3,opt,name=proposed_rule,json=proposedRule,proto3" json:"proposed_rule,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *EditDraftChunkRequest) Reset() { - *x = EditDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[220] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *EditDraftChunkRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EditDraftChunkRequest) ProtoMessage() {} - -func (x *EditDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[220] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EditDraftChunkRequest.ProtoReflect.Descriptor instead. -func (*EditDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{220} -} - -func (x *EditDraftChunkRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *EditDraftChunkRequest) GetChunkId() string { - if x != nil { - return x.ChunkId - } - return "" -} - -func (x *EditDraftChunkRequest) GetProposedRule() *sandboxv1.NetworkPolicyRule { - if x != nil { - return x.ProposedRule - } - return nil -} - -func (x *EditDraftChunkRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *EditDraftChunkRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -type EditDraftChunkResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *EditDraftChunkResponse) Reset() { - *x = EditDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[221] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *EditDraftChunkResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EditDraftChunkResponse) ProtoMessage() {} - -func (x *EditDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[221] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EditDraftChunkResponse.ProtoReflect.Descriptor instead. -func (*EditDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{221} -} - -// Reverse an approval (remove merged rule from active policy). -type UndoDraftChunkRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // Chunk ID to undo. - ChunkId string `protobuf:"bytes,2,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,4,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UndoDraftChunkRequest) Reset() { - *x = UndoDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[222] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UndoDraftChunkRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UndoDraftChunkRequest) ProtoMessage() {} - -func (x *UndoDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[222] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UndoDraftChunkRequest.ProtoReflect.Descriptor instead. -func (*UndoDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{222} -} - -func (x *UndoDraftChunkRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *UndoDraftChunkRequest) GetChunkId() string { - if x != nil { - return x.ChunkId - } - return "" -} - -func (x *UndoDraftChunkRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *UndoDraftChunkRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -type UndoDraftChunkResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // New policy version after removal. - PolicyVersion uint32 `protobuf:"varint,1,opt,name=policy_version,json=policyVersion,proto3" json:"policy_version,omitempty"` - // SHA-256 hash of the updated policy. - PolicyHash string `protobuf:"bytes,2,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UndoDraftChunkResponse) Reset() { - *x = UndoDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[223] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UndoDraftChunkResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UndoDraftChunkResponse) ProtoMessage() {} - -func (x *UndoDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[223] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UndoDraftChunkResponse.ProtoReflect.Descriptor instead. -func (*UndoDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{223} -} - -func (x *UndoDraftChunkResponse) GetPolicyVersion() uint32 { - if x != nil { - return x.PolicyVersion - } - return 0 -} - -func (x *UndoDraftChunkResponse) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -// Clear all pending draft chunks for a sandbox. -type ClearDraftChunksRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Optional nonzero UUID for durable at-most-once admission. Successful results - // can be replayed for 24 hours; see the API errors and retries reference. - RequestId string `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ClearDraftChunksRequest) Reset() { - *x = ClearDraftChunksRequest{} - mi := &file_openshell_proto_msgTypes[224] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ClearDraftChunksRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ClearDraftChunksRequest) ProtoMessage() {} - -func (x *ClearDraftChunksRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[224] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ClearDraftChunksRequest.ProtoReflect.Descriptor instead. -func (*ClearDraftChunksRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{224} -} - -func (x *ClearDraftChunksRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ClearDraftChunksRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *ClearDraftChunksRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -type ClearDraftChunksResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Number of chunks cleared. - ChunksCleared uint32 `protobuf:"varint,1,opt,name=chunks_cleared,json=chunksCleared,proto3" json:"chunks_cleared,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ClearDraftChunksResponse) Reset() { - *x = ClearDraftChunksResponse{} - mi := &file_openshell_proto_msgTypes[225] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ClearDraftChunksResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ClearDraftChunksResponse) ProtoMessage() {} - -func (x *ClearDraftChunksResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[225] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ClearDraftChunksResponse.ProtoReflect.Descriptor instead. -func (*ClearDraftChunksResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{225} -} - -func (x *ClearDraftChunksResponse) GetChunksCleared() uint32 { - if x != nil { - return x.ChunksCleared - } - return 0 -} - -// Get decision history for a sandbox's draft policy. -type GetDraftHistoryRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,2,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetDraftHistoryRequest) Reset() { - *x = GetDraftHistoryRequest{} - mi := &file_openshell_proto_msgTypes[226] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetDraftHistoryRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetDraftHistoryRequest) ProtoMessage() {} - -func (x *GetDraftHistoryRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[226] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetDraftHistoryRequest.ProtoReflect.Descriptor instead. -func (*GetDraftHistoryRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{226} -} - -func (x *GetDraftHistoryRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetDraftHistoryRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -type DraftHistoryEntry struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Time when the event occurred. - EventTime *timestamppb.Timestamp `protobuf:"bytes,101,opt,name=event_time,json=eventTime,proto3" json:"event_time,omitempty"` - // Event type: "denial_detected", "analysis_cycle", "approved", - // "rejected", "edited", "undone", "cleared". - EventType string `protobuf:"bytes,2,opt,name=event_type,json=eventType,proto3" json:"event_type,omitempty"` - // Human-readable description. - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - // Associated chunk ID (if applicable). - ChunkId string `protobuf:"bytes,4,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DraftHistoryEntry) Reset() { - *x = DraftHistoryEntry{} - mi := &file_openshell_proto_msgTypes[227] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DraftHistoryEntry) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DraftHistoryEntry) ProtoMessage() {} - -func (x *DraftHistoryEntry) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[227] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DraftHistoryEntry.ProtoReflect.Descriptor instead. -func (*DraftHistoryEntry) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{227} -} - -func (x *DraftHistoryEntry) GetEventTime() *timestamppb.Timestamp { - if x != nil { - return x.EventTime - } - return nil -} - -func (x *DraftHistoryEntry) GetEventType() string { - if x != nil { - return x.EventType - } - return "" -} - -func (x *DraftHistoryEntry) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *DraftHistoryEntry) GetChunkId() string { - if x != nil { - return x.ChunkId - } - return "" -} - -type GetDraftHistoryResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Chronological decision history. - Entries []*DraftHistoryEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetDraftHistoryResponse) Reset() { - *x = GetDraftHistoryResponse{} - mi := &file_openshell_proto_msgTypes[228] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetDraftHistoryResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetDraftHistoryResponse) ProtoMessage() {} - -func (x *GetDraftHistoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[228] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetDraftHistoryResponse.ProtoReflect.Descriptor instead. -func (*GetDraftHistoryResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{228} -} - -func (x *GetDraftHistoryResponse) GetEntries() []*DraftHistoryEntry { - if x != nil { - return x.Entries - } - return nil -} - -// Create workspace request. -type CreateWorkspaceRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace name. Must be a valid DNS-1123 label. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Optional labels for the workspace (key-value metadata). - Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Optional nonzero UUID. Same ID and payload replay success for 24 hours. - RequestId string `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateWorkspaceRequest) Reset() { - *x = CreateWorkspaceRequest{} - mi := &file_openshell_proto_msgTypes[229] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateWorkspaceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateWorkspaceRequest) ProtoMessage() {} - -func (x *CreateWorkspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[229] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateWorkspaceRequest.ProtoReflect.Descriptor instead. -func (*CreateWorkspaceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{229} -} - -func (x *CreateWorkspaceRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *CreateWorkspaceRequest) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - -func (x *CreateWorkspaceRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Create workspace response. -type CreateWorkspaceResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Workspace *datamodelv1.Workspace `protobuf:"bytes,1,opt,name=workspace,proto3" json:"workspace,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateWorkspaceResponse) Reset() { - *x = CreateWorkspaceResponse{} - mi := &file_openshell_proto_msgTypes[230] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateWorkspaceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateWorkspaceResponse) ProtoMessage() {} - -func (x *CreateWorkspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[230] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateWorkspaceResponse.ProtoReflect.Descriptor instead. -func (*CreateWorkspaceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{230} -} - -func (x *CreateWorkspaceResponse) GetWorkspace() *datamodelv1.Workspace { - if x != nil { - return x.Workspace - } - return nil -} - -// Get workspace request. -type GetWorkspaceRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace name (canonical lookup key). - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetWorkspaceRequest) Reset() { - *x = GetWorkspaceRequest{} - mi := &file_openshell_proto_msgTypes[231] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetWorkspaceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetWorkspaceRequest) ProtoMessage() {} - -func (x *GetWorkspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[231] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetWorkspaceRequest.ProtoReflect.Descriptor instead. -func (*GetWorkspaceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{231} -} - -func (x *GetWorkspaceRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// Get workspace response. -type GetWorkspaceResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Workspace *datamodelv1.Workspace `protobuf:"bytes,1,opt,name=workspace,proto3" json:"workspace,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetWorkspaceResponse) Reset() { - *x = GetWorkspaceResponse{} - mi := &file_openshell_proto_msgTypes[232] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetWorkspaceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetWorkspaceResponse) ProtoMessage() {} - -func (x *GetWorkspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[232] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetWorkspaceResponse.ProtoReflect.Descriptor instead. -func (*GetWorkspaceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{232} -} - -func (x *GetWorkspaceResponse) GetWorkspace() *datamodelv1.Workspace { - if x != nil { - return x.Workspace - } - return nil -} - -// List workspaces request. -type ListWorkspacesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The maximum number of workspaces to return. Zero uses 100. Values above - // 1000 are coerced to 1000; negative values are invalid. - PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - // Token from a previous ListWorkspaces response. All other request parameters - // except page_size must match the request that produced it. - PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` - // Optional label selector for filtering (format: "key1=value1,key2=value2"). - LabelSelector string `protobuf:"bytes,3,opt,name=label_selector,json=labelSelector,proto3" json:"label_selector,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListWorkspacesRequest) Reset() { - *x = ListWorkspacesRequest{} - mi := &file_openshell_proto_msgTypes[233] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListWorkspacesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListWorkspacesRequest) ProtoMessage() {} - -func (x *ListWorkspacesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[233] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListWorkspacesRequest.ProtoReflect.Descriptor instead. -func (*ListWorkspacesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{233} -} - -func (x *ListWorkspacesRequest) GetPageSize() int32 { - if x != nil { - return x.PageSize - } - return 0 -} - -func (x *ListWorkspacesRequest) GetPageToken() string { - if x != nil { - return x.PageToken - } - return "" -} - -func (x *ListWorkspacesRequest) GetLabelSelector() string { - if x != nil { - return x.LabelSelector - } - return "" -} - -// List workspaces response. -type ListWorkspacesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Workspaces []*datamodelv1.Workspace `protobuf:"bytes,1,rep,name=workspaces,proto3" json:"workspaces,omitempty"` - // Token for the next page. Empty when there are no subsequent pages. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListWorkspacesResponse) Reset() { - *x = ListWorkspacesResponse{} - mi := &file_openshell_proto_msgTypes[234] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListWorkspacesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListWorkspacesResponse) ProtoMessage() {} - -func (x *ListWorkspacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[234] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListWorkspacesResponse.ProtoReflect.Descriptor instead. -func (*ListWorkspacesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{234} -} - -func (x *ListWorkspacesResponse) GetWorkspaces() []*datamodelv1.Workspace { - if x != nil { - return x.Workspaces - } - return nil -} - -func (x *ListWorkspacesResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -// Delete workspace request. -type DeleteWorkspaceRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace name (canonical lookup key). - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - AllowMissing bool `protobuf:"varint,2,opt,name=allow_missing,json=allowMissing,proto3" json:"allow_missing,omitempty"` - // Optional nonzero UUID. Same ID and payload replay success for 24 hours. - RequestId string `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteWorkspaceRequest) Reset() { - *x = DeleteWorkspaceRequest{} - mi := &file_openshell_proto_msgTypes[235] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteWorkspaceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteWorkspaceRequest) ProtoMessage() {} - -func (x *DeleteWorkspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[235] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteWorkspaceRequest.ProtoReflect.Descriptor instead. -func (*DeleteWorkspaceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{235} -} - -func (x *DeleteWorkspaceRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *DeleteWorkspaceRequest) GetAllowMissing() bool { - if x != nil { - return x.AllowMissing - } - return false -} - -func (x *DeleteWorkspaceRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Delete workspace response. -type DeleteWorkspaceResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Outcome DeletionOutcome `protobuf:"varint,2,opt,name=outcome,proto3,enum=openshell.v1.DeletionOutcome" json:"outcome,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteWorkspaceResponse) Reset() { - *x = DeleteWorkspaceResponse{} - mi := &file_openshell_proto_msgTypes[236] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteWorkspaceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteWorkspaceResponse) ProtoMessage() {} - -func (x *DeleteWorkspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[236] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteWorkspaceResponse.ProtoReflect.Descriptor instead. -func (*DeleteWorkspaceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{236} -} - -func (x *DeleteWorkspaceResponse) GetOutcome() DeletionOutcome { - if x != nil { - return x.Outcome - } - return DeletionOutcome_DELETION_OUTCOME_UNSPECIFIED -} - -// Workspace membership record. -type WorkspaceMember struct { - state protoimpl.MessageState `protogen:"open.v1"` - Metadata *datamodelv1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - // OIDC subject claim identifying the principal. - PrincipalSubject string `protobuf:"bytes,2,opt,name=principal_subject,json=principalSubject,proto3" json:"principal_subject,omitempty"` - // Role assigned to the principal within the workspace. - Role WorkspaceRole `protobuf:"varint,3,opt,name=role,proto3,enum=openshell.v1.WorkspaceRole" json:"role,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WorkspaceMember) Reset() { - *x = WorkspaceMember{} - mi := &file_openshell_proto_msgTypes[237] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WorkspaceMember) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WorkspaceMember) ProtoMessage() {} - -func (x *WorkspaceMember) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[237] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WorkspaceMember.ProtoReflect.Descriptor instead. -func (*WorkspaceMember) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{237} -} - -func (x *WorkspaceMember) GetMetadata() *datamodelv1.ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *WorkspaceMember) GetPrincipalSubject() string { - if x != nil { - return x.PrincipalSubject - } - return "" -} - -func (x *WorkspaceMember) GetRole() WorkspaceRole { - if x != nil { - return x.Role - } - return WorkspaceRole_WORKSPACE_ROLE_UNSPECIFIED -} - -// Add workspace member request. -type AddWorkspaceMemberRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,1,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // OIDC subject claim identifying the principal. - PrincipalSubject string `protobuf:"bytes,2,opt,name=principal_subject,json=principalSubject,proto3" json:"principal_subject,omitempty"` - // Role to assign. - Role WorkspaceRole `protobuf:"varint,3,opt,name=role,proto3,enum=openshell.v1.WorkspaceRole" json:"role,omitempty"` - // Optional nonzero UUID. Same ID and payload replay success for 24 hours. - RequestId string `protobuf:"bytes,4,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AddWorkspaceMemberRequest) Reset() { - *x = AddWorkspaceMemberRequest{} - mi := &file_openshell_proto_msgTypes[238] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AddWorkspaceMemberRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddWorkspaceMemberRequest) ProtoMessage() {} - -func (x *AddWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[238] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddWorkspaceMemberRequest.ProtoReflect.Descriptor instead. -func (*AddWorkspaceMemberRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{238} -} - -func (x *AddWorkspaceMemberRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *AddWorkspaceMemberRequest) GetPrincipalSubject() string { - if x != nil { - return x.PrincipalSubject - } - return "" -} - -func (x *AddWorkspaceMemberRequest) GetRole() WorkspaceRole { - if x != nil { - return x.Role - } - return WorkspaceRole_WORKSPACE_ROLE_UNSPECIFIED -} - -func (x *AddWorkspaceMemberRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Add workspace member response. -type AddWorkspaceMemberResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Member *WorkspaceMember `protobuf:"bytes,1,opt,name=member,proto3" json:"member,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AddWorkspaceMemberResponse) Reset() { - *x = AddWorkspaceMemberResponse{} - mi := &file_openshell_proto_msgTypes[239] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AddWorkspaceMemberResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddWorkspaceMemberResponse) ProtoMessage() {} - -func (x *AddWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[239] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddWorkspaceMemberResponse.ProtoReflect.Descriptor instead. -func (*AddWorkspaceMemberResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{239} -} - -func (x *AddWorkspaceMemberResponse) GetMember() *WorkspaceMember { - if x != nil { - return x.Member - } - return nil -} - -// Remove workspace member request. -type RemoveWorkspaceMemberRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,1,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // OIDC subject claim identifying the principal to remove. - PrincipalSubject string `protobuf:"bytes,2,opt,name=principal_subject,json=principalSubject,proto3" json:"principal_subject,omitempty"` - AllowMissing bool `protobuf:"varint,3,opt,name=allow_missing,json=allowMissing,proto3" json:"allow_missing,omitempty"` - // Optional nonzero UUID. Same ID and payload replay success for 24 hours. - RequestId string `protobuf:"bytes,4,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RemoveWorkspaceMemberRequest) Reset() { - *x = RemoveWorkspaceMemberRequest{} - mi := &file_openshell_proto_msgTypes[240] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RemoveWorkspaceMemberRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveWorkspaceMemberRequest) ProtoMessage() {} - -func (x *RemoveWorkspaceMemberRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[240] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveWorkspaceMemberRequest.ProtoReflect.Descriptor instead. -func (*RemoveWorkspaceMemberRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{240} -} - -func (x *RemoveWorkspaceMemberRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *RemoveWorkspaceMemberRequest) GetPrincipalSubject() string { - if x != nil { - return x.PrincipalSubject - } - return "" -} - -func (x *RemoveWorkspaceMemberRequest) GetAllowMissing() bool { - if x != nil { - return x.AllowMissing - } - return false -} - -func (x *RemoveWorkspaceMemberRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -// Remove workspace member response. -type RemoveWorkspaceMemberResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Outcome DeletionOutcome `protobuf:"varint,2,opt,name=outcome,proto3,enum=openshell.v1.DeletionOutcome" json:"outcome,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RemoveWorkspaceMemberResponse) Reset() { - *x = RemoveWorkspaceMemberResponse{} - mi := &file_openshell_proto_msgTypes[241] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RemoveWorkspaceMemberResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveWorkspaceMemberResponse) ProtoMessage() {} - -func (x *RemoveWorkspaceMemberResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[241] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveWorkspaceMemberResponse.ProtoReflect.Descriptor instead. -func (*RemoveWorkspaceMemberResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{241} -} - -func (x *RemoveWorkspaceMemberResponse) GetOutcome() DeletionOutcome { - if x != nil { - return x.Outcome - } - return DeletionOutcome_DELETION_OUTCOME_UNSPECIFIED -} - -// List workspace members request. -type ListWorkspaceMembersRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Only a named workspace selection is accepted. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,1,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - // The maximum number of members to return. Zero uses 100. Values above - // 1000 are coerced to 1000; negative values are invalid. - PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - // Token from a previous ListWorkspaceMembers response. All other request - // parameters except page_size must match the request that produced it. - PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListWorkspaceMembersRequest) Reset() { - *x = ListWorkspaceMembersRequest{} - mi := &file_openshell_proto_msgTypes[242] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListWorkspaceMembersRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListWorkspaceMembersRequest) ProtoMessage() {} - -func (x *ListWorkspaceMembersRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[242] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListWorkspaceMembersRequest.ProtoReflect.Descriptor instead. -func (*ListWorkspaceMembersRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{242} -} - -func (x *ListWorkspaceMembersRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *ListWorkspaceMembersRequest) GetPageSize() int32 { - if x != nil { - return x.PageSize - } - return 0 -} - -func (x *ListWorkspaceMembersRequest) GetPageToken() string { - if x != nil { - return x.PageToken - } - return "" -} - -// List workspace members response. -type ListWorkspaceMembersResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Members []*WorkspaceMember `protobuf:"bytes,1,rep,name=members,proto3" json:"members,omitempty"` - // Token for the next page. Empty when there are no subsequent pages. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListWorkspaceMembersResponse) Reset() { - *x = ListWorkspaceMembersResponse{} - mi := &file_openshell_proto_msgTypes[243] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListWorkspaceMembersResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListWorkspaceMembersResponse) ProtoMessage() {} - -func (x *ListWorkspaceMembersResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[243] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListWorkspaceMembersResponse.ProtoReflect.Descriptor instead. -func (*ListWorkspaceMembersResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{243} -} - -func (x *ListWorkspaceMembersResponse) GetMembers() []*WorkspaceMember { - if x != nil { - return x.Members - } - return nil -} - -func (x *ListWorkspaceMembersResponse) GetNextPageToken() string { - if x != nil { - return x.NextPageToken - } - return "" -} - -// Short-lived credential for one policy-authorized extension service. -// Kept at the end of the file so adding it does not renumber existing -// generated message descriptors. -type ExtensionServiceCredential struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Operator registration name used to correlate the credential with the - // stable service registration delivered by GetSandboxConfig. - ServiceName string `protobuf:"bytes,1,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` - // Gateway-minted JWT with an audience derived from the registration. - Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` - // Absolute expiry of the token. - ExpirationTime *timestamppb.Timestamp `protobuf:"bytes,103,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExtensionServiceCredential) Reset() { - *x = ExtensionServiceCredential{} - mi := &file_openshell_proto_msgTypes[244] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExtensionServiceCredential) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExtensionServiceCredential) ProtoMessage() {} - -func (x *ExtensionServiceCredential) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[244] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExtensionServiceCredential.ProtoReflect.Descriptor instead. -func (*ExtensionServiceCredential) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{244} -} - -func (x *ExtensionServiceCredential) GetServiceName() string { - if x != nil { - return x.ServiceName - } - return "" -} - -func (x *ExtensionServiceCredential) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -func (x *ExtensionServiceCredential) GetExpirationTime() *timestamppb.Timestamp { - if x != nil { - return x.ExpirationTime - } - return nil -} - -// One redacted endpoint result in a supervisor's complete status report. -type EndpointObservation struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Stable identifier derived from the configured host, ports, and path. - EndpointId string `protobuf:"bytes,1,opt,name=endpoint_id,json=endpointId,proto3" json:"endpoint_id,omitempty"` - // Latest result under the reported configuration and supervisor session. - Result EndpointResult `protobuf:"varint,2,opt,name=result,proto3,enum=openshell.v1.EndpointResult" json:"result,omitempty"` - // Bounded cause of result, when the supervisor classified it. Each failure - // domain names the results it can explain. - Failure *EndpointFailure `protobuf:"bytes,3,opt,name=failure,proto3" json:"failure,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *EndpointObservation) Reset() { - *x = EndpointObservation{} - mi := &file_openshell_proto_msgTypes[245] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *EndpointObservation) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EndpointObservation) ProtoMessage() {} - -func (x *EndpointObservation) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[245] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EndpointObservation.ProtoReflect.Descriptor instead. -func (*EndpointObservation) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{245} -} - -func (x *EndpointObservation) GetEndpointId() string { - if x != nil { - return x.EndpointId - } - return "" -} - -func (x *EndpointObservation) GetResult() EndpointResult { - if x != nil { - return x.Result - } - return EndpointResult_ENDPOINT_RESULT_UNSPECIFIED -} - -func (x *EndpointObservation) GetFailure() *EndpointFailure { - if x != nil { - return x.Failure - } - return nil -} - -// Complete endpoint status report for the caller's current configuration. -type ReportEndpointStatusRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox id. Must match the authenticated sandbox principal. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Hash from the active effective policy delivered by the gateway. - PolicyHash string `protobuf:"bytes,2,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - // Provider environment revision delivered with the active configuration. - ProviderEnvRevision uint64 `protobuf:"varint,3,opt,name=provider_env_revision,json=providerEnvRevision,proto3" json:"provider_env_revision,omitempty"` - // Exactly one result for every distinct observed endpoint in the policy. - Observations []*EndpointObservation `protobuf:"bytes,4,rep,name=observations,proto3" json:"observations,omitempty"` - // Endpoints with a new observation in this batch. Omitted endpoints retain - // their prior report time; an identical retry never advances report time. - ObservedEndpointIds []string `protobuf:"bytes,5,rep,name=observed_endpoint_ids,json=observedEndpointIds,proto3" json:"observed_endpoint_ids,omitempty"` - // Active ConnectSupervisor session that owns these observations. - SupervisorSessionId string `protobuf:"bytes,6,opt,name=supervisor_session_id,json=supervisorSessionId,proto3" json:"supervisor_session_id,omitempty"` - // Monotonically increasing sequence within the authenticated session. Gaps - // are allowed when an inventory reset supersedes a frozen snapshot. Retrying - // a report preserves its complete body and sequence for idempotent acknowledgement. - ReportSequence uint64 `protobuf:"varint,7,opt,name=report_sequence,json=reportSequence,proto3" json:"report_sequence,omitempty"` - // Complete latest grant state. Omission retains legacy compatibility; - // a present message with no last_failure explicitly clears the prior cause. - TokenGrantStatus *TokenGrantStatus `protobuf:"bytes,8,opt,name=token_grant_status,json=tokenGrantStatus,proto3" json:"token_grant_status,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReportEndpointStatusRequest) Reset() { - *x = ReportEndpointStatusRequest{} - mi := &file_openshell_proto_msgTypes[246] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReportEndpointStatusRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportEndpointStatusRequest) ProtoMessage() {} - -func (x *ReportEndpointStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[246] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportEndpointStatusRequest.ProtoReflect.Descriptor instead. -func (*ReportEndpointStatusRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{246} -} - -func (x *ReportEndpointStatusRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *ReportEndpointStatusRequest) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *ReportEndpointStatusRequest) GetProviderEnvRevision() uint64 { - if x != nil { - return x.ProviderEnvRevision - } - return 0 -} - -func (x *ReportEndpointStatusRequest) GetObservations() []*EndpointObservation { - if x != nil { - return x.Observations - } - return nil -} - -func (x *ReportEndpointStatusRequest) GetObservedEndpointIds() []string { - if x != nil { - return x.ObservedEndpointIds - } - return nil -} - -func (x *ReportEndpointStatusRequest) GetSupervisorSessionId() string { - if x != nil { - return x.SupervisorSessionId - } - return "" -} - -func (x *ReportEndpointStatusRequest) GetReportSequence() uint64 { - if x != nil { - return x.ReportSequence - } - return 0 -} - -func (x *ReportEndpointStatusRequest) GetTokenGrantStatus() *TokenGrantStatus { - if x != nil { - return x.TokenGrantStatus - } - return nil -} - -// Latest token-grant state sent by the authenticated supervisor. -type TokenGrantStatus struct { - state protoimpl.MessageState `protogen:"open.v1"` - LastFailure *TokenGrantFailureObservation `protobuf:"bytes,1,opt,name=last_failure,json=lastFailure,proto3" json:"last_failure,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TokenGrantStatus) Reset() { - *x = TokenGrantStatus{} - mi := &file_openshell_proto_msgTypes[247] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TokenGrantStatus) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TokenGrantStatus) ProtoMessage() {} - -func (x *TokenGrantStatus) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[247] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TokenGrantStatus.ProtoReflect.Descriptor instead. -func (*TokenGrantStatus) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{247} -} - -func (x *TokenGrantStatus) GetLastFailure() *TokenGrantFailureObservation { - if x != nil { - return x.LastFailure - } - return nil -} - -// A refused grant and its destination, without request paths or remote text. -// This describes the failed attempt, not the destination's current health. -type TokenGrantFailureObservation struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Canonical observed host or IP address, bounded to 253 ASCII bytes. - DestinationHost string `protobuf:"bytes,1,opt,name=destination_host,json=destinationHost,proto3" json:"destination_host,omitempty"` - // Observed destination port in the range 1 through 65535. - DestinationPort uint32 `protobuf:"varint,2,opt,name=destination_port,json=destinationPort,proto3" json:"destination_port,omitempty"` - // Bounded token_grant cause. Provider and credential may be absent when - // selection failed; the request ID still identifies the refused exchange. - Failure *EndpointFailure `protobuf:"bytes,3,opt,name=failure,proto3" json:"failure,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TokenGrantFailureObservation) Reset() { - *x = TokenGrantFailureObservation{} - mi := &file_openshell_proto_msgTypes[248] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TokenGrantFailureObservation) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TokenGrantFailureObservation) ProtoMessage() {} - -func (x *TokenGrantFailureObservation) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[248] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TokenGrantFailureObservation.ProtoReflect.Descriptor instead. -func (*TokenGrantFailureObservation) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{248} -} - -func (x *TokenGrantFailureObservation) GetDestinationHost() string { - if x != nil { - return x.DestinationHost - } - return "" -} - -func (x *TokenGrantFailureObservation) GetDestinationPort() uint32 { - if x != nil { - return x.DestinationPort - } - return 0 -} - -func (x *TokenGrantFailureObservation) GetFailure() *EndpointFailure { - if x != nil { - return x.Failure - } - return nil -} - -// Empty acknowledgement for a persisted endpoint status report. -type ReportEndpointStatusResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReportEndpointStatusResponse) Reset() { - *x = ReportEndpointStatusResponse{} - mi := &file_openshell_proto_msgTypes[249] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReportEndpointStatusResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportEndpointStatusResponse) ProtoMessage() {} - -func (x *ReportEndpointStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[249] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportEndpointStatusResponse.ProtoReflect.Descriptor instead. -func (*ReportEndpointStatusResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{249} -} - -// A configured endpoint and its last accepted network result in one record. -// Address fields contain policy selectors, never request URLs or credentials. -type EndpointStatus struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Stable identifier for selecting this endpoint without parsing display text. - EndpointId string `protobuf:"bytes,1,opt,name=endpoint_id,json=endpointId,proto3" json:"endpoint_id,omitempty"` - // Lowercase configured endpoint host. - Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"` - // Sorted, deduplicated effective endpoint ports. Validated endpoints have at - // least one port. - Ports []uint32 `protobuf:"varint,3,rep,packed,name=ports,proto3" json:"ports,omitempty"` - // Canonical configured path selector; an unrestricted path is /**. - Path string `protobuf:"bytes,4,opt,name=path,proto3" json:"path,omitempty"` - // Last accepted result, aggregated across configured callers and ports. - // NoObservedExchange retains the address and has no report timestamp. - LastResult EndpointResult `protobuf:"varint,5,opt,name=last_result,json=lastResult,proto3,enum=openshell.v1.EndpointResult" json:"last_result,omitempty"` - // Time when the gateway accepted the observation. This is not the request - // time: still-valid evidence can be reaccepted after a reset. Identical - // same-sequence retries do not advance it. Absent until a result is reported. - LastReportedTime *timestamppb.Timestamp `protobuf:"bytes,106,opt,name=last_reported_time,json=lastReportedTime,proto3" json:"last_reported_time,omitempty"` - // Bounded cause of last_result, when the supervisor classified it. Each - // failure domain names the results it can explain. Replaced or cleared by - // every later observation, and cleared by the same resets that clear - // last_result. - LastFailure *EndpointFailure `protobuf:"bytes,7,opt,name=last_failure,json=lastFailure,proto3" json:"last_failure,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *EndpointStatus) Reset() { - *x = EndpointStatus{} - mi := &file_openshell_proto_msgTypes[250] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *EndpointStatus) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EndpointStatus) ProtoMessage() {} - -func (x *EndpointStatus) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[250] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EndpointStatus.ProtoReflect.Descriptor instead. -func (*EndpointStatus) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{250} -} - -func (x *EndpointStatus) GetEndpointId() string { - if x != nil { - return x.EndpointId - } - return "" -} - -func (x *EndpointStatus) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *EndpointStatus) GetPorts() []uint32 { - if x != nil { - return x.Ports - } - return nil -} - -func (x *EndpointStatus) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *EndpointStatus) GetLastResult() EndpointResult { - if x != nil { - return x.LastResult - } - return EndpointResult_ENDPOINT_RESULT_UNSPECIFIED -} - -func (x *EndpointStatus) GetLastReportedTime() *timestamppb.Timestamp { - if x != nil { - return x.LastReportedTime - } - return nil -} - -func (x *EndpointStatus) GetLastFailure() *EndpointFailure { - if x != nil { - return x.LastFailure - } - return nil -} - -// Durable provisioning attempt, independent of supervisor registration and polling. -type SandboxProvisioning struct { - state protoimpl.MessageState `protogen:"open.v1"` - AttemptId string `protobuf:"bytes,1,opt,name=attempt_id,json=attemptId,proto3" json:"attempt_id,omitempty"` - ConfigurationChangeId string `protobuf:"bytes,2,opt,name=configuration_change_id,json=configurationChangeId,proto3" json:"configuration_change_id,omitempty"` - ConfigurationChangeTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=configuration_change_time,json=configurationChangeTime,proto3" json:"configuration_change_time,omitempty"` - FirstRejectionTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=first_rejection_time,json=firstRejectionTime,proto3" json:"first_rejection_time,omitempty"` - // Active preparation or admission-repair deadline. Absent after Ready/timeout. - Deadline *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=deadline,proto3" json:"deadline,omitempty"` - TimeoutTime *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=timeout_time,json=timeoutTime,proto3" json:"timeout_time,omitempty"` - // Set only after both supervisor and workload compute have been reclaimed. - CleanupCompletedTime *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=cleanup_completed_time,json=cleanupCompletedTime,proto3" json:"cleanup_completed_time,omitempty"` - // A safe gateway-authored diagnostic; never a raw driver error. - CleanupError string `protobuf:"bytes,8,opt,name=cleanup_error,json=cleanupError,proto3" json:"cleanup_error,omitempty"` - // Durable backoff for interrupted or failed reclamation. - CleanupRetryTime *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=cleanup_retry_time,json=cleanupRetryTime,proto3" json:"cleanup_retry_time,omitempty"` - // Attachment edits have their own durable clock; status writes do not change it. - AttachmentChangeId string `protobuf:"bytes,10,opt,name=attachment_change_id,json=attachmentChangeId,proto3" json:"attachment_change_id,omitempty"` - AttachmentChangeTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=attachment_change_time,json=attachmentChangeTime,proto3" json:"attachment_change_time,omitempty"` - // Absolute ceiling for image preparation and initial supervisor startup. - // Set once per new attempt; progress, configuration writes, and restarts - // cannot extend it. Absent on attempts created before preparation timing. - PreparationDeadline *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=preparation_deadline,json=preparationDeadline,proto3" json:"preparation_deadline,omitempty"` - // First authenticated supervisor configuration report for this attempt. - // Starts the admission repair window. Absent while preparing, including - // after a preparation timeout. Duplicate reports never change this value. - AdmissionStartTime *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=admission_start_time,json=admissionStartTime,proto3" json:"admission_start_time,omitempty"` - // An owned driver create/start future is still pending. Claimed before - // dispatch and cleared on its actual success or error response. Monitor - // interruption, owner loss, and StopSandbox/NotFound do not clear it. - // While set, cleanup cannot complete and retry/deletion remain blocked. - // This does not strengthen a driver's transport-error settlement contract. - DriverOperationPending bool `protobuf:"varint,14,opt,name=driver_operation_pending,json=driverOperationPending,proto3" json:"driver_operation_pending,omitempty"` - // Unique for each claimed driver operation, including recovery and automatic - // restart. Retained after settlement to reject delayed callbacks from an - // earlier operation on the same attempt and runtime generation. - DriverOperationId string `protobuf:"bytes,15,opt,name=driver_operation_id,json=driverOperationId,proto3" json:"driver_operation_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxProvisioning) Reset() { - *x = SandboxProvisioning{} - mi := &file_openshell_proto_msgTypes[251] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxProvisioning) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxProvisioning) ProtoMessage() {} - -func (x *SandboxProvisioning) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[251] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxProvisioning.ProtoReflect.Descriptor instead. -func (*SandboxProvisioning) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{251} -} - -func (x *SandboxProvisioning) GetAttemptId() string { - if x != nil { - return x.AttemptId - } - return "" -} - -func (x *SandboxProvisioning) GetConfigurationChangeId() string { - if x != nil { - return x.ConfigurationChangeId - } - return "" -} - -func (x *SandboxProvisioning) GetConfigurationChangeTime() *timestamppb.Timestamp { - if x != nil { - return x.ConfigurationChangeTime - } - return nil -} - -func (x *SandboxProvisioning) GetFirstRejectionTime() *timestamppb.Timestamp { - if x != nil { - return x.FirstRejectionTime - } - return nil -} - -func (x *SandboxProvisioning) GetDeadline() *timestamppb.Timestamp { - if x != nil { - return x.Deadline - } - return nil -} - -func (x *SandboxProvisioning) GetTimeoutTime() *timestamppb.Timestamp { - if x != nil { - return x.TimeoutTime - } - return nil -} - -func (x *SandboxProvisioning) GetCleanupCompletedTime() *timestamppb.Timestamp { - if x != nil { - return x.CleanupCompletedTime - } - return nil -} - -func (x *SandboxProvisioning) GetCleanupError() string { - if x != nil { - return x.CleanupError - } - return "" -} - -func (x *SandboxProvisioning) GetCleanupRetryTime() *timestamppb.Timestamp { - if x != nil { - return x.CleanupRetryTime - } - return nil -} - -func (x *SandboxProvisioning) GetAttachmentChangeId() string { - if x != nil { - return x.AttachmentChangeId - } - return "" -} - -func (x *SandboxProvisioning) GetAttachmentChangeTime() *timestamppb.Timestamp { - if x != nil { - return x.AttachmentChangeTime - } - return nil -} - -func (x *SandboxProvisioning) GetPreparationDeadline() *timestamppb.Timestamp { - if x != nil { - return x.PreparationDeadline - } - return nil -} - -func (x *SandboxProvisioning) GetAdmissionStartTime() *timestamppb.Timestamp { - if x != nil { - return x.AdmissionStartTime - } - return nil -} - -func (x *SandboxProvisioning) GetDriverOperationPending() bool { - if x != nil { - return x.DriverOperationPending - } - return false -} - -func (x *SandboxProvisioning) GetDriverOperationId() string { - if x != nil { - return x.DriverOperationId - } - return "" -} - -// Create-time request to expose one loopback HTTP service in a sandbox. -type SandboxServiceExposure struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Service name within the sandbox. Empty selects the unnamed endpoint. - Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` - // Loopback TCP port inside the sandbox. - TargetPort uint32 `protobuf:"varint,2,opt,name=target_port,json=targetPort,proto3" json:"target_port,omitempty"` - // Application authorization behavior. Omission resolves to STRIP. - AuthorizationMode ServiceAuthorizationMode `protobuf:"varint,3,opt,name=authorization_mode,json=authorizationMode,proto3,enum=openshell.v1.ServiceAuthorizationMode" json:"authorization_mode,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxServiceExposure) Reset() { - *x = SandboxServiceExposure{} - mi := &file_openshell_proto_msgTypes[252] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxServiceExposure) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxServiceExposure) ProtoMessage() {} - -func (x *SandboxServiceExposure) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[252] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxServiceExposure.ProtoReflect.Descriptor instead. -func (*SandboxServiceExposure) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{252} -} - -func (x *SandboxServiceExposure) GetService() string { - if x != nil { - return x.Service - } - return "" -} - -func (x *SandboxServiceExposure) GetTargetPort() uint32 { - if x != nil { - return x.TargetPort - } - return 0 -} - -func (x *SandboxServiceExposure) GetAuthorizationMode() ServiceAuthorizationMode { - if x != nil { - return x.AuthorizationMode - } - return ServiceAuthorizationMode_SERVICE_AUTHORIZATION_MODE_UNSPECIFIED -} - -// Bounded cause of a failed endpoint observation, including failures before -// any request reaches the endpoint. No field carries remote -// text: code and stage come from the domain's closed sets, provider and -// credential_key are configured names, and request_id is an opaque random ID. -type EndpointFailure struct { - state protoimpl.MessageState `protogen:"open.v1"` - Domain EndpointFailureDomain `protobuf:"varint,1,opt,name=domain,proto3,enum=openshell.v1.EndpointFailureDomain" json:"domain,omitempty"` - // Stable lower_snake_case code from the domain's closed set, such as - // provider_authorization_required. - Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"` - // Who can resolve the failure; always the action documented for the code. - RecoveryAction ProviderCredentialRefreshRecoveryAction `protobuf:"varint,3,opt,name=recovery_action,json=recoveryAction,proto3,enum=openshell.v1.ProviderCredentialRefreshRecoveryAction" json:"recovery_action,omitempty"` - // Provider whose credential failed. Empty when the failure happened before - // a credential was selected. - Provider string `protobuf:"bytes,4,opt,name=provider,proto3" json:"provider,omitempty"` - // Credential name within the provider. Never a credential value. - CredentialKey string `protobuf:"bytes,5,opt,name=credential_key,json=credentialKey,proto3" json:"credential_key,omitempty"` - // Step that observed the failure, from the domain's closed set. - Stage string `protobuf:"bytes,6,opt,name=stage,proto3" json:"stage,omitempty"` - // HTTP status a token service returned, when one was received. - UpstreamHttpStatus *uint32 `protobuf:"varint,7,opt,name=upstream_http_status,json=upstreamHttpStatus,proto3,oneof" json:"upstream_http_status,omitempty"` - // Supervisor-minted UUID of the refused exchange. The workload receives the - // same value in the x-openshell-request-id response header. - RequestId string `protobuf:"bytes,8,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *EndpointFailure) Reset() { - *x = EndpointFailure{} - mi := &file_openshell_proto_msgTypes[253] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *EndpointFailure) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EndpointFailure) ProtoMessage() {} - -func (x *EndpointFailure) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[253] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EndpointFailure.ProtoReflect.Descriptor instead. -func (*EndpointFailure) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{253} -} - -func (x *EndpointFailure) GetDomain() EndpointFailureDomain { - if x != nil { - return x.Domain - } - return EndpointFailureDomain_ENDPOINT_FAILURE_DOMAIN_UNSPECIFIED -} - -func (x *EndpointFailure) GetCode() string { - if x != nil { - return x.Code - } - return "" -} - -func (x *EndpointFailure) GetRecoveryAction() ProviderCredentialRefreshRecoveryAction { - if x != nil { - return x.RecoveryAction - } - return ProviderCredentialRefreshRecoveryAction_PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED -} - -func (x *EndpointFailure) GetProvider() string { - if x != nil { - return x.Provider - } - return "" -} - -func (x *EndpointFailure) GetCredentialKey() string { - if x != nil { - return x.CredentialKey - } - return "" -} - -func (x *EndpointFailure) GetStage() string { - if x != nil { - return x.Stage - } - return "" -} - -func (x *EndpointFailure) GetUpstreamHttpStatus() uint32 { - if x != nil && x.UpstreamHttpStatus != nil { - return *x.UpstreamHttpStatus - } - return 0 -} - -func (x *EndpointFailure) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -var File_openshell_proto protoreflect.FileDescriptor - -const file_openshell_proto_rawDesc = "" + - "\n" + - "\x0fopenshell.proto\x12\fopenshell.v1\x1a\x0fdatamodel.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\roptions.proto\x1a\rsandbox.proto\"\x1a\n" + - "\x18IssueSandboxTokenRequest\"\x91\x01\n" + - "\x19IssueSandboxTokenResponse\x12\x1a\n" + - "\x05token\x18\x01 \x01(\tB\x04\x88\xb5\x18\x01R\x05token\x12C\n" + - "\x0fexpiration_time\x18f \x01(\v2\x1a.google.protobuf.TimestampR\x0eexpirationTimeJ\x04\b\x02\x10\x03R\rexpires_at_ms\"T\n" + - "\x1aRefreshSandboxTokenRequest\x126\n" + - "\x17extension_service_names\x18\x01 \x03(\tR\x15extensionServiceNames\"\xd8\x03\n" + - "\x1bRefreshSandboxTokenResponse\x12\x1a\n" + - "\x05token\x18\x01 \x01(\tB\x04\x88\xb5\x18\x01R\x05token\x12C\n" + - "\x0fexpiration_time\x18f \x01(\v2\x1a.google.protobuf.TimestampR\x0eexpirationTime\x12]\n" + - "\x15extension_credentials\x18\x03 \x03(\v2(.openshell.v1.ExtensionServiceCredentialR\x14extensionCredentials\x12)\n" + - "\rsandbox_token\x18\x04 \x01(\tB\x04\x88\xb5\x18\x01R\fsandboxToken\x12R\n" + - "\x17sandbox_expiration_time\x18i \x01(\v2\x1a.google.protobuf.TimestampR\x15sandboxExpirationTime\x12\x1d\n" + - "\n" + - "session_id\x18\x06 \x01(\tR\tsessionId\x12)\n" + - "\x10credential_epoch\x18\a \x01(\x04R\x0fcredentialEpochJ\x04\b\x02\x10\x03J\x04\b\x05\x10\x06R\rexpires_at_msR\x15sandbox_expires_at_ms\"\x0f\n" + - "\rHealthRequest\"_\n" + - "\x0eHealthResponse\x123\n" + - "\x06status\x18\x01 \x01(\x0e2\x1b.openshell.v1.ServiceStatusR\x06status\x12\x18\n" + - "\aversion\x18\x02 \x01(\tR\aversion\"\x17\n" + - "\x15GetCurrentUserRequest\"\xb0\x01\n" + - "\x16GetCurrentUserResponse\x12\x18\n" + - "\asubject\x18\x01 \x01(\tR\asubject\x12!\n" + - "\fdisplay_name\x18\x02 \x01(\tR\vdisplayName\x12\x14\n" + - "\x05roles\x18\x03 \x03(\tR\x05roles\x12\x16\n" + - "\x06scopes\x18\x04 \x03(\tR\x06scopes\x12+\n" + - "\x11identity_provider\x18\x05 \x01(\tR\x10identityProvider\"\x17\n" + - "\x15GetGatewayInfoRequest\"\x87\x02\n" + - "\x16GetGatewayInfoResponse\x123\n" + - "\x06status\x18\x01 \x01(\x0e2\x1b.openshell.v1.ServiceStatusR\x06status\x12'\n" + - "\x0fgateway_version\x18\x02 \x01(\tR\x0egatewayVersion\x12H\n" + - "\x0fcompute_drivers\x18\x03 \x03(\v2\x1f.openshell.v1.ComputeDriverInfoR\x0ecomputeDrivers\x12E\n" + - "\n" + - "extensions\x18\x04 \x03(\v2%.openshell.v1.NegotiatedExtensionInfoR\n" + - "extensions\"\x95\x03\n" + - "\x17NegotiatedExtensionInfo\x12/\n" + - "\x04kind\x18\x01 \x01(\x0e2\x1b.openshell.v1.ExtensionKindR\x04kind\x12'\n" + - "\x0fconfigured_name\x18\x02 \x01(\tR\x0econfiguredName\x12/\n" + - "\x13implementation_name\x18\x03 \x01(\tR\x12implementationName\x125\n" + - "\x16implementation_version\x18\x04 \x01(\tR\x15implementationVersion\x12%\n" + - "\x0eprotocol_major\x18\x05 \x01(\rR\rprotocolMajor\x12%\n" + - "\x0eprotocol_minor\x18\x06 \x01(\rR\rprotocolMinor\x125\n" + - "\x16supported_capabilities\x18\a \x03(\tR\x15supportedCapabilities\x123\n" + - "\x15required_capabilities\x18\b \x03(\tR\x14requiredCapabilities\"t\n" + - "\x11ComputeDriverInfo\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12K\n" + - "\fcapabilities\x18\x02 \x01(\v2'.openshell.v1.ComputeDriverCapabilitiesR\fcapabilities\"\xbc\x01\n" + - "\x19ComputeDriverCapabilities\x12\x1f\n" + - "\vdriver_name\x18\x01 \x01(\tR\n" + - "driverName\x12%\n" + - "\x0edriver_version\x18\x02 \x01(\tR\rdriverVersion\x12W\n" + - "\x15resource_capabilities\x18\x03 \x01(\v2\".openshell.v1.ResourceCapabilitiesR\x14resourceCapabilities\"\xca\x01\n" + - "\x14ResourceCapabilities\x127\n" + - "\x03cpu\x18\x01 \x01(\v2%.openshell.v1.CpuResourceCapabilitiesR\x03cpu\x12@\n" + - "\x06memory\x18\x02 \x01(\v2(.openshell.v1.MemoryResourceCapabilitiesR\x06memory\x127\n" + - "\x03gpu\x18\x03 \x01(\v2%.openshell.v1.GpuResourceCapabilitiesR\x03gpu\"B\n" + - "\x17CpuResourceCapabilities\x12'\n" + - "\x0flimit_supported\x18\x01 \x01(\bR\x0elimitSupported\"E\n" + - "\x1aMemoryResourceCapabilities\x12'\n" + - "\x0flimit_supported\x18\x01 \x01(\bR\x0elimitSupported\"\x95\x01\n" + - "\x17GpuResourceCapabilities\x12>\n" + - "\x1bdefault_selection_supported\x18\x01 \x01(\bR\x19defaultSelectionSupported\x12:\n" + - "\x19count_selection_supported\x18\x02 \x01(\bR\x17countSelectionSupported\"\x80\x03\n" + - "\aSandbox\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12-\n" + - "\x04spec\x18\x02 \x01(\v2\x19.openshell.v1.SandboxSpecR\x04spec\x123\n" + - "\x06status\x18\x03 \x01(\v2\x1b.openshell.v1.SandboxStatusR\x06status\x120\n" + - "\x14host_key_fingerprint\x18\x06 \x01(\tR\x12hostKeyFingerprint\x12t\n" + - "\x1ecreated_from_workload_template\x18\x14 \x01(\v2/.openshell.v1.SandboxWorkloadTemplateProvenanceR\x1bcreatedFromWorkloadTemplateJ\x04\b\x04\x10\x05J\x04\b\x05\x10\x06R\x05phaseR\x16current_policy_version\"\x8a\x05\n" + - "\vSandboxSpec\x12\x1b\n" + - "\tlog_level\x18\x01 \x01(\tR\blogLevel\x12L\n" + - "\venvironment\x18\x05 \x03(\v2*.openshell.v1.SandboxSpec.EnvironmentEntryR\venvironment\x129\n" + - "\btemplate\x18\x06 \x01(\v2\x1d.openshell.v1.SandboxTemplateR\btemplate\x12;\n" + - "\x06policy\x18\a \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x06policy\x12\x1c\n" + - "\tproviders\x18\b \x03(\tR\tproviders\x12W\n" + - "\x15resource_requirements\x18\t \x01(\v2\".openshell.v1.ResourceRequirementsR\x14resourceRequirements\x12\x18\n" + - "\acommand\x18\f \x03(\tR\acommand\x12\x10\n" + - "\x03tty\x18\r \x01(\bR\x03tty\x12:\n" + - "\x19provider_attachment_epoch\x18\x0e \x01(\tR\x17providerAttachmentEpoch\x12I\n" + - "\x0erestart_policy\x18\x0f \x01(\x0e2\".openshell.v1.SandboxRestartPolicyR\rrestartPolicy\x1a>\n" + - "\x10EnvironmentEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01J\x04\b\n" + - "\x10\vJ\x04\b\v\x10\fR\n" + - "gpu_deviceR\x16proposal_approval_mode\"O\n" + - "\x14ResourceRequirements\x127\n" + - "\x03gpu\x18\x01 \x01(\v2%.openshell.v1.GpuResourceRequirementsR\x03gpu\">\n" + - "\x17GpuResourceRequirements\x12\x19\n" + - "\x05count\x18\x01 \x01(\rH\x00R\x05count\x88\x01\x01B\b\n" + - "\x06_count\"\xef\x05\n" + - "\x0fSandboxTemplate\x12\x14\n" + - "\x05image\x18\x01 \x01(\tR\x05image\x12,\n" + - "\x12runtime_class_name\x18\x02 \x01(\tR\x10runtimeClassName\x12!\n" + - "\fagent_socket\x18\x03 \x01(\tR\vagentSocket\x12A\n" + - "\x06labels\x18\x04 \x03(\v2).openshell.v1.SandboxTemplate.LabelsEntryR\x06labels\x12P\n" + - "\vannotations\x18\x05 \x03(\v2..openshell.v1.SandboxTemplate.AnnotationsEntryR\vannotations\x12P\n" + - "\venvironment\x18\x06 \x03(\v2..openshell.v1.SandboxTemplate.EnvironmentEntryR\venvironment\x125\n" + - "\tresources\x18\a \x01(\v2\x17.google.protobuf.StructR\tresources\x12,\n" + - "\x0fuser_namespaces\x18\n" + - " \x01(\bH\x00R\x0euserNamespaces\x88\x01\x01\x12<\n" + - "\rdriver_config\x18\v \x01(\v2\x17.google.protobuf.StructR\fdriverConfig\x1a9\n" + - "\vLabelsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a>\n" + - "\x10AnnotationsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a>\n" + - "\x10EnvironmentEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x12\n" + - "\x10_user_namespacesJ\x04\b\t\x10\n" + - "R\x16volume_claim_templates\"\x98\x01\n" + - "\x17SandboxWorkloadTemplate\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12=\n" + - "\x04spec\x18\x02 \x01(\v2).openshell.v1.SandboxWorkloadTemplateSpecR\x04spec\"\xf3\x01\n" + - "\x1bSandboxWorkloadTemplateSpec\x12?\n" + - "\bworkload\x18\x01 \x01(\v2#.openshell.v1.SandboxWorkloadConfigR\bworkload\x12<\n" + - "\rdriver_config\x18\x02 \x01(\v2\x17.google.protobuf.StructR\fdriverConfig\x12U\n" + - "\x15desired_service_level\x18\x03 \x01(\v2!.openshell.v1.SandboxServiceLevelR\x13desiredServiceLevel\"\x83\x02\n" + - "\x15SandboxWorkloadConfig\x12\x14\n" + - "\x05image\x18\x01 \x01(\tR\x05image\x12V\n" + - "\venvironment\x18\x02 \x03(\v24.openshell.v1.SandboxWorkloadConfig.EnvironmentEntryR\venvironment\x12<\n" + - "\tresources\x18\x03 \x01(\v2\x1e.openshell.v1.SandboxResourcesR\tresources\x1a>\n" + - "\x10EnvironmentEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"u\n" + - "\x10SandboxResources\x12\x10\n" + - "\x03cpu\x18\x01 \x01(\tR\x03cpu\x12\x16\n" + - "\x06memory\x18\x02 \x01(\tR\x06memory\x127\n" + - "\x03gpu\x18\x03 \x01(\v2%.openshell.v1.GpuResourceRequirementsR\x03gpu\"M\n" + - "\x13SandboxServiceLevel\x126\n" + - "\astartup\x18\x01 \x01(\v2\x1c.openshell.v1.SandboxStartupR\astartup\"k\n" + - "\x0eSandboxStartup\x12<\n" + - "\fready_within\x18\x01 \x01(\v2\x19.google.protobuf.DurationR\vreadyWithin\x12\x1b\n" + - "\tmax_burst\x18\x02 \x01(\rR\bmaxBurst\"b\n" + - "!SandboxWorkloadTemplateProvenance\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12)\n" + - "\x10resource_version\x18\x02 \x01(\tR\x0fresourceVersion\"\xf2\a\n" + - "\rSandboxStatus\x12\x1b\n" + - "\tagent_pod\x18\x02 \x01(\tR\bagentPod\x12\x19\n" + - "\bagent_fd\x18\x03 \x01(\tR\aagentFd\x12\x1d\n" + - "\n" + - "sandbox_fd\x18\x04 \x01(\tR\tsandboxFd\x12>\n" + - "\n" + - "conditions\x18\x05 \x03(\v2\x1e.openshell.v1.SandboxConditionR\n" + - "conditions\x120\n" + - "\x05phase\x18\x06 \x01(\x0e2\x1a.openshell.v1.SandboxPhaseR\x05phase\x124\n" + - "\x16current_policy_version\x18\a \x01(\rR\x14currentPolicyVersion\x127\n" + - "\x18main_process_instance_id\x18\b \x01(\tR\x15mainProcessInstanceId\x12 \n" + - "\texit_code\x18\t \x01(\x05H\x00R\bexitCode\x88\x01\x01\x12I\n" + - "\x11endpoint_statuses\x18\n" + - " \x03(\v2\x1c.openshell.v1.EndpointStatusR\x10endpointStatuses\x12d\n" + - "\x17configuration_admission\x18\v \x01(\v2+.openshell.v1.SandboxConfigurationAdmissionR\x16configurationAdmission\x12<\n" + - "\x17configuration_activated\x18\f \x01(\bH\x01R\x16configurationActivated\x88\x01\x01\x12E\n" + - "\fprovisioning\x18\r \x01(\v2!.openshell.v1.SandboxProvisioningR\fprovisioning\x12#\n" + - "\rrestart_count\x18\x0e \x01(\rR\frestartCount\x12F\n" + - "\x11next_restart_time\x18\x0f \x01(\v2\x1a.google.protobuf.TimestampR\x0fnextRestartTime\x12U\n" + - "\x19main_process_started_time\x18\x10 \x01(\v2\x1a.google.protobuf.TimestampR\x16mainProcessStartedTime\x12c\n" + - "\x18last_token_grant_failure\x18\x11 \x01(\v2*.openshell.v1.TokenGrantFailureObservationR\x15lastTokenGrantFailureB\f\n" + - "\n" + - "_exit_codeB\x1a\n" + - "\x18_configuration_activated\"\xd1\x01\n" + - "\x10SandboxCondition\x12\x12\n" + - "\x04type\x18\x01 \x01(\tR\x04type\x12\x16\n" + - "\x06status\x18\x02 \x01(\tR\x06status\x12\x16\n" + - "\x06reason\x18\x03 \x01(\tR\x06reason\x12\x18\n" + - "\amessage\x18\x04 \x01(\tR\amessage\x12C\n" + - "\x0ftransition_time\x18i \x01(\v2\x1a.google.protobuf.TimestampR\x0etransitionTimeJ\x04\b\x05\x10\x06R\x14last_transition_time\"\xc0\x02\n" + - "\rPlatformEvent\x129\n" + - "\n" + - "event_time\x18e \x01(\v2\x1a.google.protobuf.TimestampR\teventTime\x12\x16\n" + - "\x06source\x18\x02 \x01(\tR\x06source\x12\x12\n" + - "\x04type\x18\x03 \x01(\tR\x04type\x12\x16\n" + - "\x06reason\x18\x04 \x01(\tR\x06reason\x12\x18\n" + - "\amessage\x18\x05 \x01(\tR\amessage\x12E\n" + - "\bmetadata\x18\x06 \x03(\v2).openshell.v1.PlatformEvent.MetadataEntryR\bmetadata\x1a;\n" + - "\rMetadataEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01J\x04\b\x01\x10\x02R\ftimestamp_ms\"\xa9\x05\n" + - "\x14CreateSandboxRequest\x12R\n" + - "\x0fworkspace_scope\x18\a \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12-\n" + - "\x04spec\x18\x01 \x01(\v2\x19.openshell.v1.SandboxSpecR\x04spec\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\x12F\n" + - "\x06labels\x18\x03 \x03(\v2..openshell.v1.CreateSandboxRequest.LabelsEntryR\x06labels\x12U\n" + - "\vannotations\x18\x04 \x03(\v23.openshell.v1.CreateSandboxRequest.AnnotationsEntryR\vannotations\x12A\n" + - "\x1dawait_main_process_attachment\x18\x05 \x01(\bR\x1aawaitMainProcessAttachment\x12+\n" + - "\x11workload_template\x18\x06 \x01(\tR\x10workloadTemplate\x12\x1d\n" + - "\n" + - "request_id\x18\b \x01(\tR\trequestId\x12Q\n" + - "\x11service_exposures\x18\t \x03(\v2$.openshell.v1.SandboxServiceExposureR\x10serviceExposures\x1a9\n" + - "\vLabelsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a>\n" + - "\x10AnnotationsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xf8\x03\n" + - "\x14UpdateSandboxRequest\x12R\n" + - "\x0fworkspace_scope\x18\x01 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\x12\x19\n" + - "\x05image\x18\x03 \x01(\tH\x00R\x05image\x88\x01\x01\x12<\n" + - "\acommand\x18\x06 \x01(\v2\".openshell.v1.SandboxCommandUpdateR\acommand\x12@\n" + - "\venvironment\x18\a \x01(\v2\x1e.openshell.v1.SandboxMapUpdateR\venvironment\x126\n" + - "\x06labels\x18\b \x01(\v2\x1e.openshell.v1.SandboxMapUpdateR\x06labels\x12@\n" + - "\vannotations\x18\t \x01(\v2\x1e.openshell.v1.SandboxMapUpdateR\vannotations\x12:\n" + - "\x19expected_resource_version\x18\x04 \x01(\x04R\x17expectedResourceVersion\x12\x1d\n" + - "\n" + - "request_id\x18\x05 \x01(\tR\trequestIdB\b\n" + - "\x06_image\"*\n" + - "\x14SandboxCommandUpdate\x12\x12\n" + - "\x04argv\x18\x01 \x03(\tR\x04argv\"\x91\x01\n" + - "\x10SandboxMapUpdate\x12B\n" + - "\x06values\x18\x01 \x03(\v2*.openshell.v1.SandboxMapUpdate.ValuesEntryR\x06values\x1a9\n" + - "\vValuesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xd4\x01\n" + - "\x1cCreateSandboxTemplateRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12A\n" + - "\btemplate\x18\x01 \x01(\v2%.openshell.v1.SandboxWorkloadTemplateR\btemplate\x12\x1d\n" + - "\n" + - "request_id\x18\x03 \x01(\tR\trequestId\"\x83\x01\n" + - "\x19GetSandboxTemplateRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"\xd4\x01\n" + - "\x1bListSandboxTemplatesRequest\x12R\n" + - "\x0fworkspace_scope\x18\x04 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x1b\n" + - "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" + - "\n" + - "page_token\x18\x02 \x01(\tR\tpageToken\x12%\n" + - "\x0elabel_selector\x18\x03 \x01(\tR\rlabelSelector\"\xca\x01\n" + - "\x1cDeleteSandboxTemplateRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12#\n" + - "\rallow_missing\x18\x03 \x01(\bR\fallowMissing\x12\x1d\n" + - "\n" + - "request_id\x18\x04 \x01(\tR\trequestId\"\\\n" + - "\x17SandboxTemplateResponse\x12A\n" + - "\btemplate\x18\x01 \x01(\v2%.openshell.v1.SandboxWorkloadTemplateR\btemplate\"\x8b\x01\n" + - "\x1cListSandboxTemplatesResponse\x12C\n" + - "\ttemplates\x18\x01 \x03(\v2%.openshell.v1.SandboxWorkloadTemplateR\ttemplates\x12&\n" + - "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"g\n" + - "\x1dDeleteSandboxTemplateResponse\x127\n" + - "\aoutcome\x18\x02 \x01(\x0e2\x1d.openshell.v1.DeletionOutcomeR\aoutcomeJ\x04\b\x01\x10\x02R\adeleted\"\xae\x01\n" + - "\x1cBeginRootfsTarStagingRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x1b\n" + - "\tfile_name\x18\x01 \x01(\tR\bfileName\x12\x1d\n" + - "\n" + - "size_bytes\x18\x02 \x01(\x04R\tsizeBytes\"\xdc\x01\n" + - "\x1dBeginRootfsTarStagingResponse\x12#\n" + - "\rstaging_token\x18\x01 \x01(\tR\fstagingToken\x12\x1f\n" + - "\vupload_path\x18\x02 \x01(\tR\n" + - "uploadPath\x12\x1b\n" + - "\tmax_bytes\x18\x03 \x01(\x04R\bmaxBytes\x12C\n" + - "\x0fexpiration_time\x18h \x01(\v2\x1a.google.protobuf.TimestampR\x0eexpirationTimeJ\x04\b\x04\x10\x05R\rexpires_at_ms\"{\n" + - "\x11GetSandboxRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"\xcd\x01\n" + - "\x14ListSandboxesRequest\x12R\n" + - "\x0fworkspace_scope\x18\x04 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x1b\n" + - "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" + - "\n" + - "page_token\x18\x02 \x01(\tR\tpageToken\x12%\n" + - "\x0elabel_selector\x18\x03 \x01(\tR\rlabelSelector\"\xc7\x01\n" + - "\x1bListSandboxProvidersRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1b\n" + - "\tpage_size\x18\x03 \x01(\x05R\bpageSize\x12\x1d\n" + - "\n" + - "page_token\x18\x04 \x01(\tR\tpageToken\"\x83\x02\n" + - "\x1cAttachSandboxProviderRequest\x12R\n" + - "\x0fworkspace_scope\x18\x04 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1a\n" + - "\bprovider\x18\x02 \x01(\tR\bprovider\x12:\n" + - "\x19expected_resource_version\x18\x03 \x01(\x04R\x17expectedResourceVersion\x12\x1d\n" + - "\n" + - "request_id\x18\x05 \x01(\tR\trequestId\"\x83\x02\n" + - "\x1cDetachSandboxProviderRequest\x12R\n" + - "\x0fworkspace_scope\x18\x04 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1a\n" + - "\bprovider\x18\x02 \x01(\tR\bprovider\x12:\n" + - "\x19expected_resource_version\x18\x03 \x01(\x04R\x17expectedResourceVersion\x12\x1d\n" + - "\n" + - "request_id\x18\x05 \x01(\tR\trequestId\"\xc2\x01\n" + - "\x14DeleteSandboxRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12#\n" + - "\rallow_missing\x18\x03 \x01(\bR\fallowMissing\x12\x1d\n" + - "\n" + - "request_id\x18\x04 \x01(\tR\trequestId\"\x9b\x01\n" + - "\x12StopSandboxRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n" + - "\n" + - "request_id\x18\x03 \x01(\tR\trequestId\"\x9c\x01\n" + - "\x13StartSandboxRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n" + - "\n" + - "request_id\x18\x03 \x01(\tR\trequestId\"\x7f\n" + - "\x15SuspendSandboxRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"\xb7\x01\n" + - "\x14ResumeSandboxRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x127\n" + - "\x18restart_if_restore_fails\x18\x03 \x01(\bR\x15restartIfRestoreFails\"\xd5\x01\n" + - "\x0fSandboxResponse\x12/\n" + - "\asandbox\x18\x01 \x01(\v2\x15.openshell.v1.SandboxR\asandbox\x12Q\n" + - "\fservice_urls\x18\x02 \x03(\v2..openshell.v1.SandboxResponse.ServiceUrlsEntryR\vserviceUrls\x1a>\n" + - "\x10ServiceUrlsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"t\n" + - "\x15ListSandboxesResponse\x123\n" + - "\tsandboxes\x18\x01 \x03(\v2\x15.openshell.v1.SandboxR\tsandboxes\x12&\n" + - "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\x86\x01\n" + - "\x1cListSandboxProvidersResponse\x12>\n" + - "\tproviders\x18\x01 \x03(\v2 .openshell.datamodel.v1.ProviderR\tproviders\x12&\n" + - "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\xad\x01\n" + - "\x1dAttachSandboxProviderResponse\x12/\n" + - "\asandbox\x18\x01 \x01(\v2\x15.openshell.v1.SandboxR\asandbox\x12\x1a\n" + - "\battached\x18\x02 \x01(\bR\battached\x12?\n" + - "\areceipt\x18\x03 \x01(\v2%.openshell.v1.ProviderMutationReceiptR\areceipt\"\xad\x01\n" + - "\x1dDetachSandboxProviderResponse\x12/\n" + - "\asandbox\x18\x01 \x01(\v2\x15.openshell.v1.SandboxR\asandbox\x12\x1a\n" + - "\bdetached\x18\x02 \x01(\bR\bdetached\x12?\n" + - "\areceipt\x18\x03 \x01(\v2%.openshell.v1.ProviderMutationReceiptR\areceipt\"\xd8\x02\n" + - "\x17ProviderDesiredIdentity\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x18\n" + - "\asandbox\x18\x02 \x01(\tR\asandbox\x12)\n" + - "\x10attachment_epoch\x18\x03 \x01(\tR\x0fattachmentEpoch\x12\x1f\n" + - "\vprovider_id\x18\x04 \x01(\tR\n" + - "providerId\x12:\n" + - "\x19provider_resource_version\x18\x05 \x01(\x04R\x17providerResourceVersion\x122\n" + - "\x15provider_env_revision\x18\x06 \x01(\x04R\x13providerEnvRevision\x12'\n" + - "\x0fconfig_revision\x18\a \x01(\x04R\x0econfigRevision\x12\x1f\n" + - "\vpolicy_hash\x18\b \x01(\tR\n" + - "policyHash\"\xfa\x01\n" + - "\x16ConfigSnapshotRevision\x12L\n" + - "\x0esandbox_config\x18\x01 \x01(\v2#.openshell.v1.SandboxConfigRevisionH\x00R\rsandboxConfig\x123\n" + - "\x14provider_environment\x18\x02 \x01(\x04H\x00R\x13providerEnvironment\x12P\n" + - "\x0fprovider_target\x18\x03 \x01(\v2%.openshell.v1.ProviderDesiredIdentityH\x00R\x0eproviderTargetB\v\n" + - "\tcomponent\"\x91\x02\n" + - "\x15SandboxConfigRevision\x12'\n" + - "\x0fconfig_revision\x18\x01 \x01(\x04R\x0econfigRevision\x12%\n" + - "\x0epolicy_version\x18\x02 \x01(\rR\rpolicyVersion\x12G\n" + - "\rpolicy_source\x18\x03 \x01(\x0e2\".openshell.sandbox.v1.PolicySourceR\fpolicySource\x122\n" + - "\x15global_policy_version\x18\x04 \x01(\rR\x13globalPolicyVersion\x12+\n" + - "\x11settings_revision\x18\x05 \x01(\x04R\x10settingsRevision\"\x8c\x05\n" + - "\x15ConfigUpdateOperation\x12!\n" + - "\foperation_id\x18\x01 \x01(\tR\voperationId\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x02 \x01(\tR\tsandboxId\x12;\n" + - "\tcomponent\x18\x03 \x01(\x0e2\x1d.openshell.v1.ConfigComponentR\tcomponent\x12M\n" + - "\x0ftarget_revision\x18\x04 \x01(\v2$.openshell.v1.ConfigSnapshotRevisionR\x0etargetRevision\x12>\n" + - "\x05state\x18\x05 \x01(\x0e2(.openshell.v1.ConfigUpdateOperationStateR\x05state\x12:\n" + - "\aoutcome\x18\x06 \x01(\x0e2 .openshell.v1.ConfigApplyOutcomeR\aoutcome\x12'\n" + - "\x0fsanitized_error\x18\a \x01(\tR\x0esanitizedError\x12=\n" + - "\fcreated_time\x18l \x01(\v2\x1a.google.protobuf.TimestampR\vcreatedTime\x12=\n" + - "\fupdated_time\x18m \x01(\v2\x1a.google.protobuf.TimestampR\vupdatedTime\x12A\n" + - "\x0ecompleted_time\x18n \x01(\v2\x1a.google.protobuf.TimestampR\rcompletedTimeJ\x04\b\b\x10\tJ\x04\b\t\x10\n" + - "J\x04\b\n" + - "\x10\vR\rcreated_at_msR\rupdated_at_msR\x0fcompleted_at_ms\"\xe6\x02\n" + - "\x17ProviderMutationReceipt\x12\x1d\n" + - "\n" + - "receipt_id\x18\x01 \x01(\tR\treceiptId\x12\x1f\n" + - "\vmutation_id\x18\x02 \x01(\tR\n" + - "mutationId\x12\x1a\n" + - "\bprovider\x18\x03 \x01(\tR\bprovider\x12\x1c\n" + - "\tworkspace\x18\x04 \x01(\tR\tworkspace\x126\n" + - "\x04kind\x18\x05 \x01(\x0e2\".openshell.v1.ProviderMutationKindR\x04kind\x12?\n" + - "\adesired\x18\x06 \x01(\v2%.openshell.v1.ProviderDesiredIdentityR\adesired\x12A\n" + - "\x0epersisted_time\x18k \x01(\v2\x1a.google.protobuf.TimestampR\rpersistedTimeJ\x04\b\a\x10\bR\x0fpersisted_at_ms\"\x8d\x04\n" + - "\x1cProviderReadinessObservation\x12\x1d\n" + - "\n" + - "session_id\x18\x01 \x01(\tR\tsessionId\x12\x1a\n" + - "\bsequence\x18\x02 \x01(\x04R\bsequence\x12)\n" + - "\x10attachment_epoch\x18\x03 \x01(\tR\x0fattachmentEpoch\x122\n" + - "\x15provider_env_revision\x18\x04 \x01(\x04R\x13providerEnvRevision\x12'\n" + - "\x0fconfig_revision\x18\x05 \x01(\x04R\x0econfigRevision\x12\x1f\n" + - "\vpolicy_hash\x18\x06 \x01(\tR\n" + - "policyHash\x123\n" + - "\x15credentials_installed\x18\a \x01(\bR\x14credentialsInstalled\x12#\n" + - "\rpolicy_active\x18\b \x01(\bR\fpolicyActive\x12@\n" + - "\x1claunch_environment_installed\x18\t \x01(\bR\x1alaunchEnvironmentInstalled\x12.\n" + - "\x13process_instance_id\x18\n" + - " \x01(\tR\x11processInstanceId\x12=\n" + - "\x06reason\x18\v \x01(\x0e2%.openshell.v1.ProviderReadinessReasonR\x06reason\"\xc1\x04\n" + - "\x17ProviderReadinessStatus\x12?\n" + - "\areceipt\x18\x01 \x01(\v2%.openshell.v1.ProviderMutationReceiptR\areceipt\x12:\n" + - "\x05state\x18\x02 \x01(\x0e2$.openshell.v1.ProviderReadinessStateR\x05state\x12=\n" + - "\x06reason\x18\x03 \x01(\x0e2%.openshell.v1.ProviderReadinessReasonR\x06reason\x12F\n" + - "\bobserved\x18\x04 \x01(\v2*.openshell.v1.ProviderReadinessObservationR\bobserved\x12.\n" + - "\x13network_instance_id\x18\x05 \x01(\tR\x11networkInstanceId\x12?\n" + - "\robserved_time\x18j \x01(\v2\x1a.google.protobuf.TimestampR\fobservedTime\x12A\n" + - "\x0eevaluated_time\x18k \x01(\v2\x1a.google.protobuf.TimestampR\revaluatedTime\x12A\n" + - "\toperation\x18\b \x01(\v2#.openshell.v1.ConfigUpdateOperationR\toperationJ\x04\b\x06\x10\aJ\x04\b\a\x10\bR\x0eobserved_at_msR\x0fevaluated_at_ms\"\xca\x01\n" + - "\x1fGetSandboxProviderStatusRequest\x12R\n" + - "\x0fworkspace_scope\x18\x04 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1a\n" + - "\bprovider\x18\x02 \x01(\tR\bprovider\x12\x1d\n" + - "\n" + - "receipt_id\x18\x03 \x01(\tR\treceiptId\"a\n" + - " GetSandboxProviderStatusResponse\x12=\n" + - "\x06status\x18\x01 \x01(\v2%.openshell.v1.ProviderReadinessStatusR\x06status\"\x8d\x01\n" + - "\x1eReportProviderReadinessRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12L\n" + - "\vobservation\x18\x02 \x01(\v2*.openshell.v1.ProviderReadinessObservationR\vobservation\"\x94\x02\n" + - "\x1fReportProviderReadinessResponse\x12+\n" + - "\x11accepted_sequence\x18\x01 \x01(\x04R\x10acceptedSequence\x12B\n" + - "\x0freport_interval\x18f \x01(\v2\x19.google.protobuf.DurationR\x0ereportInterval\x12B\n" + - "\x0fobservation_ttl\x18g \x01(\v2\x19.google.protobuf.DurationR\x0eobservationTtlJ\x04\b\x02\x10\x03J\x04\b\x03\x10\x04R\x17report_interval_secondsR\x17observation_ttl_seconds\"~\n" + - "\x15DeleteSandboxResponse\x127\n" + - "\aoutcome\x18\x02 \x01(\x0e2\x1d.openshell.v1.DeletionOutcomeR\aoutcome\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x03 \x01(\tR\tsandboxIdJ\x04\b\x01\x10\x02R\adeleted\"\x87\x01\n" + - "\x17CreateSshSessionRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\"\xce\x02\n" + - "\x18CreateSshSessionResponse\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x1a\n" + - "\x05token\x18\x02 \x01(\tB\x04\x88\xb5\x18\x01R\x05token\x12!\n" + - "\fgateway_host\x18\x03 \x01(\tR\vgatewayHost\x12!\n" + - "\fgateway_port\x18\x04 \x01(\rR\vgatewayPort\x12%\n" + - "\x0egateway_scheme\x18\x05 \x01(\tR\rgatewayScheme\x120\n" + - "\x14host_key_fingerprint\x18\a \x01(\tR\x12hostKeyFingerprint\x12C\n" + - "\x0fexpiration_time\x18l \x01(\v2\x1a.google.protobuf.TimestampR\x0eexpirationTimeJ\x04\b\b\x10\tR\rexpires_at_ms\"\xc7\x02\n" + - "\x14ExposeServiceRequest\x12R\n" + - "\x0fworkspace_scope\x18\x05 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\x12\x1f\n" + - "\vtarget_port\x18\x03 \x01(\rR\n" + - "targetPort\x12\x16\n" + - "\x06domain\x18\x04 \x01(\bR\x06domain\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1d\n" + - "\n" + - "request_id\x18\x06 \x01(\tR\trequestId\x12U\n" + - "\x12authorization_mode\x18\a \x01(\x0e2&.openshell.v1.ServiceAuthorizationModeR\x11authorizationMode\"\x95\x01\n" + - "\x11GetServiceRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\"\xbf\x01\n" + - "\x13ListServicesRequest\x12R\n" + - "\x0fworkspace_scope\x18\x04 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x1b\n" + - "\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12\x1d\n" + - "\n" + - "page_token\x18\x03 \x01(\tR\tpageToken\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\"\x81\x01\n" + - "\x14ListServicesResponse\x12A\n" + - "\bservices\x18\x01 \x03(\v2%.openshell.v1.ServiceEndpointResponseR\bservices\x12&\n" + - "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\xdc\x01\n" + - "\x14DeleteServiceRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12#\n" + - "\rallow_missing\x18\x04 \x01(\bR\fallowMissing\x12\x1d\n" + - "\n" + - "request_id\x18\x05 \x01(\tR\trequestId\"_\n" + - "\x15DeleteServiceResponse\x127\n" + - "\aoutcome\x18\x02 \x01(\x0e2\x1d.openshell.v1.DeletionOutcomeR\aoutcomeJ\x04\b\x01\x10\x02R\adeleted\"\xae\x02\n" + - "\x0fServiceEndpoint\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x02 \x01(\tR\tsandboxId\x12\x18\n" + - "\asandbox\x18\x03 \x01(\tR\asandbox\x12\x12\n" + - "\x04name\x18\x04 \x01(\tR\x04name\x12\x1f\n" + - "\vtarget_port\x18\x05 \x01(\rR\n" + - "targetPort\x12\x16\n" + - "\x06domain\x18\x06 \x01(\bR\x06domain\x12U\n" + - "\x12authorization_mode\x18\a \x01(\x0e2&.openshell.v1.ServiceAuthorizationModeR\x11authorizationMode\"f\n" + - "\x17ServiceEndpointResponse\x129\n" + - "\bendpoint\x18\x01 \x01(\v2\x1d.openshell.v1.ServiceEndpointR\bendpoint\x12\x10\n" + - "\x03url\x18\x02 \x01(\tR\x03url\"Z\n" + - "\x17RevokeSshSessionRequest\x12\x1a\n" + - "\x05token\x18\x01 \x01(\tB\x04\x88\xb5\x18\x01R\x05token\x12#\n" + - "\rallow_missing\x18\x02 \x01(\bR\fallowMissing\"b\n" + - "\x18RevokeSshSessionResponse\x127\n" + - "\aoutcome\x18\x02 \x01(\x0e2\x1d.openshell.v1.DeletionOutcomeR\aoutcomeJ\x04\b\x01\x10\x02R\arevoked\"\xbf\x04\n" + - "\x12ExecSandboxRequest\x12R\n" + - "\x0fworkspace_scope\x18\f \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x18\n" + - "\acommand\x18\x02 \x03(\tR\acommand\x12\x18\n" + - "\aworkdir\x18\x03 \x01(\tR\aworkdir\x12S\n" + - "\venvironment\x18\x04 \x03(\v21.openshell.v1.ExecSandboxRequest.EnvironmentEntryR\venvironment\x12F\n" + - "\x11execution_timeout\x18i \x01(\v2\x19.google.protobuf.DurationR\x10executionTimeout\x12\x14\n" + - "\x05stdin\x18\x06 \x01(\fR\x05stdin\x12\x10\n" + - "\x03tty\x18\a \x01(\bR\x03tty\x12\x12\n" + - "\x04cols\x18\b \x01(\rR\x04cols\x12\x12\n" + - "\x04rows\x18\t \x01(\rR\x04rows\x12$\n" + - "\x0eno_login_shell\x18\n" + - " \x01(\bR\fnoLoginShell\x12\x1d\n" + - "\n" + - "request_id\x18\v \x01(\tR\trequestId\x1a>\n" + - "\x10EnvironmentEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01J\x04\b\x05\x10\x06R\x0ftimeout_seconds\"'\n" + - "\x11ExecSandboxStdout\x12\x12\n" + - "\x04data\x18\x01 \x01(\fR\x04data\"'\n" + - "\x11ExecSandboxStderr\x12\x12\n" + - "\x04data\x18\x01 \x01(\fR\x04data\".\n" + - "\x0fExecSandboxExit\x12\x1b\n" + - "\texit_code\x18\x01 \x01(\x05R\bexitCode\"\xc8\x01\n" + - "\x10ExecSandboxEvent\x129\n" + - "\x06stdout\x18\x01 \x01(\v2\x1f.openshell.v1.ExecSandboxStdoutH\x00R\x06stdout\x129\n" + - "\x06stderr\x18\x02 \x01(\v2\x1f.openshell.v1.ExecSandboxStderrH\x00R\x06stderr\x123\n" + - "\x04exit\x18\x03 \x01(\v2\x1d.openshell.v1.ExecSandboxExitH\x00R\x04exitB\t\n" + - "\apayload\"\x8c\x02\n" + - "\x0eTcpForwardInit\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1c\n" + - "\tworkspace\x18\x03 \x01(\tR\tworkspace\x12\x1d\n" + - "\n" + - "service_id\x18\x04 \x01(\tR\tserviceId\x120\n" + - "\x03ssh\x18\x05 \x01(\v2\x1c.openshell.v1.SshRelayTargetH\x00R\x03ssh\x120\n" + - "\x03tcp\x18\x06 \x01(\v2\x1c.openshell.v1.TcpRelayTargetH\x00R\x03tcp\x125\n" + - "\x13authorization_token\x18\a \x01(\tB\x04\x88\xb5\x18\x01R\x12authorizationTokenB\b\n" + - "\x06target\"f\n" + - "\x0fTcpForwardFrame\x122\n" + - "\x04init\x18\x01 \x01(\v2\x1c.openshell.v1.TcpForwardInitH\x00R\x04init\x12\x14\n" + - "\x04data\x18\x02 \x01(\fH\x00R\x04dataB\t\n" + - "\apayload\"\xb0\x01\n" + - "\x10ExecSandboxInput\x128\n" + - "\x05start\x18\x01 \x01(\v2 .openshell.v1.ExecSandboxRequestH\x00R\x05start\x12\x16\n" + - "\x05stdin\x18\x02 \x01(\fH\x00R\x05stdin\x12?\n" + - "\x06resize\x18\x03 \x01(\v2%.openshell.v1.ExecSandboxWindowResizeH\x00R\x06resizeB\t\n" + - "\apayload\"A\n" + - "\x17ExecSandboxWindowResize\x12\x12\n" + - "\x04cols\x18\x01 \x01(\rR\x04cols\x12\x12\n" + - "\x04rows\x18\x02 \x01(\rR\x04rows\"\xfb\x01\n" + - "\n" + - "SshSession\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x02 \x01(\tR\tsandboxId\x12\x1a\n" + - "\x05token\x18\x03 \x01(\tB\x04\x88\xb5\x18\x01R\x05token\x12C\n" + - "\x0fexpiration_time\x18h \x01(\v2\x1a.google.protobuf.TimestampR\x0eexpirationTime\x12\x18\n" + - "\arevoked\x18\x05 \x01(\bR\arevokedJ\x04\b\x04\x10\x05R\rexpires_at_ms\"\xa1\x04\n" + - "\x13WatchSandboxRequest\x12R\n" + - "\x0fworkspace_scope\x18\v \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12#\n" + - "\rfollow_status\x18\x02 \x01(\bR\ffollowStatus\x12\x1f\n" + - "\vfollow_logs\x18\x03 \x01(\bR\n" + - "followLogs\x12#\n" + - "\rfollow_events\x18\x04 \x01(\bR\ffollowEvents\x12$\n" + - "\x0elog_tail_lines\x18\x05 \x01(\rR\flogTailLines\x12\x1d\n" + - "\n" + - "event_tail\x18\x06 \x01(\rR\teventTail\x12(\n" + - "\x10stop_on_terminal\x18\a \x01(\bR\x0estopOnTerminal\x129\n" + - "\n" + - "since_time\x18l \x01(\v2\x1a.google.protobuf.TimestampR\tsinceTime\x12\x1f\n" + - "\vlog_sources\x18\t \x03(\tR\n" + - "logSources\x12\"\n" + - "\rlog_min_level\x18\n" + - " \x01(\tR\vlogMinLevel\x12.\n" + - "\x13resume_after_cursor\x18\f \x01(\tR\x11resumeAfterCursorJ\x04\b\b\x10\tR\flog_since_ms\"\xe4\x02\n" + - "\x12SandboxStreamEvent\x121\n" + - "\asandbox\x18\x01 \x01(\v2\x15.openshell.v1.SandboxH\x00R\asandbox\x120\n" + - "\x03log\x18\x02 \x01(\v2\x1c.openshell.v1.SandboxLogLineH\x00R\x03log\x123\n" + - "\x05event\x18\x03 \x01(\v2\x1b.openshell.v1.PlatformEventH\x00R\x05event\x12>\n" + - "\awarning\x18\x04 \x01(\v2\".openshell.v1.SandboxStreamWarningH\x00R\awarning\x12Q\n" + - "\x13draft_policy_update\x18\x05 \x01(\v2\x1f.openshell.v1.DraftPolicyUpdateH\x00R\x11draftPolicyUpdate\x12\x16\n" + - "\x06cursor\x18\x06 \x01(\tR\x06cursorB\t\n" + - "\apayload\"\xdb\x02\n" + - "\x0eSandboxLogLine\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x129\n" + - "\n" + - "event_time\x18f \x01(\v2\x1a.google.protobuf.TimestampR\teventTime\x12\x14\n" + - "\x05level\x18\x03 \x01(\tR\x05level\x12\x16\n" + - "\x06target\x18\x04 \x01(\tR\x06target\x12\x18\n" + - "\amessage\x18\x05 \x01(\tR\amessage\x12\x16\n" + - "\x06source\x18\x06 \x01(\tR\x06source\x12@\n" + - "\x06fields\x18\a \x03(\v2(.openshell.v1.SandboxLogLine.FieldsEntryR\x06fields\x1a9\n" + - "\vFieldsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01J\x04\b\x02\x10\x03R\ftimestamp_ms\"0\n" + - "\x14SandboxStreamWarning\x12\x18\n" + - "\amessage\x18\x01 \x01(\tR\amessage\"\xc8\x01\n" + - "\x15CreateProviderRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12<\n" + - "\bprovider\x18\x01 \x01(\v2 .openshell.datamodel.v1.ProviderR\bprovider\x12\x1d\n" + - "\n" + - "request_id\x18\x03 \x01(\tR\trequestId\"|\n" + - "\x12GetProviderRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"\x89\x02\n" + - "\x1dGetProviderCredentialsRequest\x12R\n" + - "\x0fworkspace_scope\x18\x01 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\x12'\n" + - "\x0fcredential_keys\x18\x03 \x03(\tR\x0ecredentialKeys\x12W\n" + - "\x1aminimum_remaining_lifetime\x18\x04 \x01(\v2\x19.google.protobuf.DurationR\x18minimumRemainingLifetime\"z\n" + - "\x17ProviderCredentialValue\x12\x1a\n" + - "\x05value\x18\x01 \x01(\tB\x04\x88\xb5\x18\x01R\x05value\x12C\n" + - "\x0fexpiration_time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x0eexpirationTime\"\xe8\x01\n" + - "\x1eGetProviderCredentialsResponse\x12_\n" + - "\vcredentials\x18\x01 \x03(\v2=.openshell.v1.GetProviderCredentialsResponse.CredentialsEntryR\vcredentials\x1ae\n" + - "\x10CredentialsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12;\n" + - "\x05value\x18\x02 \x01(\v2%.openshell.v1.ProviderCredentialValueR\x05value:\x028\x01\"\xcd\x01\n" + - "\x14ListProvidersRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x1b\n" + - "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" + - "\n" + - "page_token\x18\x02 \x01(\tR\tpageToken\x12%\n" + - "\x0elabel_selector\x18\x04 \x01(\tR\rlabelSelector\"\xa0\x04\n" + - "\x15UpdateProviderRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12<\n" + - "\bprovider\x18\x01 \x01(\v2 .openshell.datamodel.v1.ProviderR\bprovider\x12\x82\x01\n" + - "\x1bcredential_expiration_times\x18f \x03(\v2B.openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntryR\x19credentialExpirationTimes\x12G\n" + - " clear_credential_expiration_keys\x18g \x03(\tR\x1dclearCredentialExpirationKeys\x12\x1d\n" + - "\n" + - "request_id\x18\x04 \x01(\tR\trequestId\x1ah\n" + - "\x1eCredentialExpirationTimesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x120\n" + - "\x05value\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x05value:\x028\x01J\x04\b\x02\x10\x03R\x18credential_expires_at_ms\"\xc3\x01\n" + - "\x15DeleteProviderRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12#\n" + - "\rallow_missing\x18\x04 \x01(\bR\fallowMissing\x12\x1d\n" + - "\n" + - "request_id\x18\x05 \x01(\tR\trequestId\"\xc1\x01\n" + - "\x10ProviderResponse\x12<\n" + - "\bprovider\x18\x01 \x01(\v2 .openshell.datamodel.v1.ProviderR\bprovider\x12N\n" + - "\x0ftarget_receipts\x18\x02 \x03(\v2%.openshell.v1.ProviderMutationReceiptR\x0etargetReceipts\x12\x1f\n" + - "\vmutation_id\x18\x03 \x01(\tR\n" + - "mutationId\"\x7f\n" + - "\x15ListProvidersResponse\x12>\n" + - "\tproviders\x18\x01 \x03(\v2 .openshell.datamodel.v1.ProviderR\tproviders\x12&\n" + - "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\xad\x01\n" + - "\x1bListProviderProfilesRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x1b\n" + - "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" + - "\n" + - "page_token\x18\x02 \x01(\tR\tpageToken\"\x7f\n" + - "\x19GetProviderProfileRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\"l\n" + - "\x19ProviderProfileImportItem\x127\n" + - "\aprofile\x18\x01 \x01(\v2\x1d.openshell.v1.ProviderProfileR\aprofile\x12\x16\n" + - "\x06source\x18\x02 \x01(\tR\x06source\"\x9e\x01\n" + - "\x19ProviderProfileDiagnostic\x12\x16\n" + - "\x06source\x18\x01 \x01(\tR\x06source\x12\x1d\n" + - "\n" + - "profile_id\x18\x02 \x01(\tR\tprofileId\x12\x14\n" + - "\x05field\x18\x03 \x01(\tR\x05field\x12\x18\n" + - "\amessage\x18\x04 \x01(\tR\amessage\x12\x1a\n" + - "\bseverity\x18\x05 \x01(\tR\bseverity\"\x9e\x01\n" + - ",ProviderCredentialTokenGrantAudienceOverride\x12\x12\n" + - "\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\x02 \x01(\rR\x04port\x12\x12\n" + - "\x04path\x18\x03 \x01(\tR\x04path\x12\x1a\n" + - "\baudience\x18\x04 \x01(\tR\baudience\x12\x16\n" + - "\x06scopes\x18\x05 \x03(\tR\x06scopes\"\x90\x01\n" + - "(ProviderCredentialTokenGrantSubjectToken\x12\x16\n" + - "\x06source\x18\x01 \x01(\tR\x06source\x12\x1e\n" + - "\n" + - "credential\x18\x02 \x01(\tR\n" + - "credential\x12,\n" + - "\x12subject_token_type\x18\x03 \x01(\tR\x10subjectTokenType\"\xf3\x04\n" + - "\x1cProviderCredentialTokenGrant\x12%\n" + - "\x0etoken_endpoint\x18\x01 \x01(\tR\rtokenEndpoint\x12\x1a\n" + - "\baudience\x18\x02 \x01(\tR\baudience\x12*\n" + - "\x11jwt_svid_audience\x18\x06 \x01(\tR\x0fjwtSvidAudience\x12\x16\n" + - "\x06scopes\x18\x03 \x03(\tR\x06scopes\x126\n" + - "\tcache_ttl\x18h \x01(\v2\x19.google.protobuf.DurationR\bcacheTtl\x12i\n" + - "\x12audience_overrides\x18\x05 \x03(\v2:.openshell.v1.ProviderCredentialTokenGrantAudienceOverrideR\x11audienceOverrides\x122\n" + - "\x15client_assertion_type\x18\a \x01(\tR\x13clientAssertionType\x12M\n" + - "\n" + - "grant_type\x18\b \x01(\x0e2..openshell.v1.ProviderCredentialTokenGrantTypeR\tgrantType\x12[\n" + - "\rsubject_token\x18\t \x01(\v26.openshell.v1.ProviderCredentialTokenGrantSubjectTokenR\fsubjectToken\x120\n" + - "\x14requested_token_type\x18\n" + - " \x01(\tR\x12requestedTokenTypeJ\x04\b\x04\x10\x05R\x11cache_ttl_seconds\"\xfb\x03\n" + - "\x19ProviderProfileCredential\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12 \n" + - "\vdescription\x18\x02 \x01(\tR\vdescription\x12\x19\n" + - "\benv_vars\x18\x03 \x03(\tR\aenvVars\x12\x1a\n" + - "\brequired\x18\x04 \x01(\bR\brequired\x12\x1d\n" + - "\n" + - "auth_style\x18\x05 \x01(\tR\tauthStyle\x12\x1f\n" + - "\vheader_name\x18\x06 \x01(\tR\n" + - "headerName\x12\x1f\n" + - "\vquery_param\x18\a \x01(\tR\n" + - "queryParam\x12A\n" + - "\arefresh\x18\b \x01(\v2'.openshell.v1.ProviderCredentialRefreshR\arefresh\x12#\n" + - "\rpath_template\x18\t \x01(\tR\fpathTemplate\x12K\n" + - "\vtoken_grant\x18\n" + - " \x01(\v2*.openshell.v1.ProviderCredentialTokenGrantR\n" + - "tokenGrant\x12,\n" + - "\x12token_grant_owners\x18\v \x03(\tR\x10tokenGrantOwners\x12-\n" + - "\x12stable_placeholder\x18\f \x01(\bR\x11stablePlaceholder\"\x8d\x01\n" + - "!ProviderCredentialRefreshMaterial\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12 \n" + - "\vdescription\x18\x02 \x01(\tR\vdescription\x12\x1a\n" + - "\brequired\x18\x03 \x01(\bR\brequired\x12\x16\n" + - "\x06secret\x18\x04 \x01(\bR\x06secret\"Y\n" + - "\x1fProviderCredentialRefreshOutput\x12\x16\n" + - "\x06output\x18\x01 \x01(\tR\x06output\x12\x1e\n" + - "\n" + - "credential\x18\x02 \x01(\tR\n" + - "credential\"\x82\x04\n" + - "\x19ProviderCredentialRefresh\x12K\n" + - "\bstrategy\x18\x01 \x01(\x0e2/.openshell.v1.ProviderCredentialRefreshStrategyR\bstrategy\x12\x1b\n" + - "\ttoken_url\x18\x02 \x01(\tR\btokenUrl\x12\x16\n" + - "\x06scopes\x18\x03 \x03(\tR\x06scopes\x12@\n" + - "\x0erefresh_before\x18h \x01(\v2\x19.google.protobuf.DurationR\rrefreshBefore\x12<\n" + - "\fmax_lifetime\x18i \x01(\v2\x19.google.protobuf.DurationR\vmaxLifetime\x12K\n" + - "\bmaterial\x18\x06 \x03(\v2/.openshell.v1.ProviderCredentialRefreshMaterialR\bmaterial\x12\\\n" + - "\x12additional_outputs\x18\a \x03(\v2-.openshell.v1.ProviderCredentialRefreshOutputR\x11additionalOutputsJ\x04\b\x04\x10\x05J\x04\b\x05\x10\x06R\x16refresh_before_secondsR\x14max_lifetime_seconds\"\xbc\x06\n" + - "\x1fProviderCredentialRefreshStatus\x12\x1a\n" + - "\bprovider\x18\x01 \x01(\tR\bprovider\x12\x1f\n" + - "\vprovider_id\x18\x02 \x01(\tR\n" + - "providerId\x12%\n" + - "\x0ecredential_key\x18\x03 \x01(\tR\rcredentialKey\x12K\n" + - "\bstrategy\x18\x04 \x01(\x0e2/.openshell.v1.ProviderCredentialRefreshStrategyR\bstrategy\x12\x16\n" + - "\x06status\x18\x05 \x01(\tR\x06status\x12C\n" + - "\x0fexpiration_time\x18j \x01(\v2\x1a.google.protobuf.TimestampR\x0eexpirationTime\x12F\n" + - "\x11next_refresh_time\x18k \x01(\v2\x1a.google.protobuf.TimestampR\x0fnextRefreshTime\x12F\n" + - "\x11last_refresh_time\x18l \x01(\v2\x1a.google.protobuf.TimestampR\x0flastRefreshTime\x12\x1d\n" + - "\n" + - "last_error\x18\t \x01(\tR\tlastError\x12^\n" + - "\x0frecovery_action\x18\n" + - " \x01(\x0e25.openshell.v1.ProviderCredentialRefreshRecoveryActionR\x0erecoveryAction\x12!\n" + - "\ffailure_code\x18\v \x01(\tR\vfailureCode\x124\n" + - "\x16provider_error_subtype\x18\f \x01(\tR\x14providerErrorSubtype\x12B\n" + - "\x0flast_error_time\x18q \x01(\v2\x1a.google.protobuf.TimestampR\rlastErrorTimeJ\x04\b\x06\x10\aJ\x04\b\a\x10\bJ\x04\b\b\x10\tJ\x04\b\r\x10\x0eR\rexpires_at_msR\x12next_refresh_at_msR\x12last_refresh_at_msR\x10last_error_at_ms\"<\n" + - "\x18ProviderProfileDiscovery\x12 \n" + - "\vcredentials\x18\x01 \x03(\tR\vcredentials\"\xb8\x01\n" + - "\x1fGetProviderRefreshStatusRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x1a\n" + - "\bprovider\x18\x01 \x01(\tR\bprovider\x12%\n" + - "\x0ecredential_key\x18\x02 \x01(\tR\rcredentialKey\"s\n" + - " GetProviderRefreshStatusResponse\x12O\n" + - "\vcredentials\x18\x01 \x03(\v2-.openshell.v1.ProviderCredentialRefreshStatusR\vcredentials\"\xcc\x04\n" + - "\x1fConfigureProviderRefreshRequest\x12R\n" + - "\x0fworkspace_scope\x18\a \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x1a\n" + - "\bprovider\x18\x01 \x01(\tR\bprovider\x12%\n" + - "\x0ecredential_key\x18\x02 \x01(\tR\rcredentialKey\x12K\n" + - "\bstrategy\x18\x03 \x01(\x0e2/.openshell.v1.ProviderCredentialRefreshStrategyR\bstrategy\x12]\n" + - "\bmaterial\x18\x04 \x03(\v2;.openshell.v1.ConfigureProviderRefreshRequest.MaterialEntryB\x04\x88\xb5\x18\x01R\bmaterial\x120\n" + - "\x14secret_material_keys\x18\x05 \x03(\tR\x12secretMaterialKeys\x12C\n" + - "\x0fexpiration_time\x18j \x01(\v2\x1a.google.protobuf.TimestampR\x0eexpirationTime\x12\x1d\n" + - "\n" + - "request_id\x18\b \x01(\tR\trequestId\x1a;\n" + - "\rMaterialEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01J\x04\b\x06\x10\aR\rexpires_at_ms\"i\n" + - " ConfigureProviderRefreshResponse\x12E\n" + - "\x06status\x18\x01 \x01(\v2-.openshell.v1.ProviderCredentialRefreshStatusR\x06status\"\xd7\x01\n" + - "\x1fRotateProviderCredentialRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x1a\n" + - "\bprovider\x18\x01 \x01(\tR\bprovider\x12%\n" + - "\x0ecredential_key\x18\x02 \x01(\tR\rcredentialKey\x12\x1d\n" + - "\n" + - "request_id\x18\x04 \x01(\tR\trequestId\"i\n" + - " RotateProviderCredentialResponse\x12E\n" + - "\x06status\x18\x01 \x01(\v2-.openshell.v1.ProviderCredentialRefreshStatusR\x06status\"\xf9\x01\n" + - "\x1cDeleteProviderRefreshRequest\x12R\n" + - "\x0fworkspace_scope\x18\x04 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x1a\n" + - "\bprovider\x18\x01 \x01(\tR\bprovider\x12%\n" + - "\x0ecredential_key\x18\x02 \x01(\tR\rcredentialKey\x12#\n" + - "\rallow_missing\x18\x05 \x01(\bR\fallowMissing\x12\x1d\n" + - "\n" + - "request_id\x18\x06 \x01(\tR\trequestId\"g\n" + - "\x1dDeleteProviderRefreshResponse\x127\n" + - "\aoutcome\x18\x02 \x01(\x0e2\x1d.openshell.v1.DeletionOutcomeR\aoutcomeJ\x04\b\x01\x10\x02R\adeleted\"\x91\x06\n" + - "\x0fProviderProfile\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12!\n" + - "\fdisplay_name\x18\x02 \x01(\tR\vdisplayName\x12 \n" + - "\vdescription\x18\x03 \x01(\tR\vdescription\x12A\n" + - "\bcategory\x18\x04 \x01(\x0e2%.openshell.v1.ProviderProfileCategoryR\bcategory\x12I\n" + - "\vcredentials\x18\x05 \x03(\v2'.openshell.v1.ProviderProfileCredentialR\vcredentials\x12C\n" + - "\tendpoints\x18\x06 \x03(\v2%.openshell.sandbox.v1.NetworkEndpointR\tendpoints\x12?\n" + - "\bbinaries\x18\a \x03(\v2#.openshell.sandbox.v1.NetworkBinaryR\bbinaries\x12+\n" + - "\x11inference_capable\x18\b \x01(\bR\x10inferenceCapable\x12D\n" + - "\tdiscovery\x18\t \x01(\v2&.openshell.v1.ProviderProfileDiscoveryR\tdiscovery\x12)\n" + - "\x10resource_version\x18\n" + - " \x01(\x04R\x0fresourceVersion\x12P\n" + - "\vannotations\x18\v \x03(\v2..openshell.v1.ProviderProfile.AnnotationsEntryR\vannotations\x12\x16\n" + - "\x06source\x18\f \x01(\tR\x06source\x12\x14\n" + - "\x05scope\x18\r \x01(\tR\x05scope\x127\n" + - "\x05files\x18\x0e \x03(\v2!.openshell.v1.ProviderProfileFileR\x05files\x1a>\n" + - "\x10AnnotationsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\\\n" + - "\x13ProviderProfileFile\x12\x12\n" + - "\x04path\x18\x01 \x01(\tR\x04path\x12\x18\n" + - "\acontent\x18\x02 \x01(\tR\acontent\x12\x17\n" + - "\aenv_var\x18\x03 \x01(\tR\x06envVar\"R\n" + - "\x17ProviderProfileResponse\x127\n" + - "\aprofile\x18\x01 \x01(\v2\x1d.openshell.v1.ProviderProfileR\aprofile\"\x81\x01\n" + - "\x1cListProviderProfilesResponse\x129\n" + - "\bprofiles\x18\x01 \x03(\v2\x1d.openshell.v1.ProviderProfileR\bprofiles\x12&\n" + - "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\xd7\x01\n" + - "\x1dImportProviderProfilesRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12C\n" + - "\bprofiles\x18\x01 \x03(\v2'.openshell.v1.ProviderProfileImportItemR\bprofiles\x12\x1d\n" + - "\n" + - "request_id\x18\x03 \x01(\tR\trequestId\"\xc2\x01\n" + - "\x1eImportProviderProfilesResponse\x12I\n" + - "\vdiagnostics\x18\x01 \x03(\v2'.openshell.v1.ProviderProfileDiagnosticR\vdiagnostics\x129\n" + - "\bprofiles\x18\x02 \x03(\v2\x1d.openshell.v1.ProviderProfileR\bprofiles\x12\x1a\n" + - "\bimported\x18\x03 \x01(\bR\bimported\"\xa1\x02\n" + - "\x1dUpdateProviderProfilesRequest\x12R\n" + - "\x0fworkspace_scope\x18\x04 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12A\n" + - "\aprofile\x18\x01 \x01(\v2'.openshell.v1.ProviderProfileImportItemR\aprofile\x12:\n" + - "\x19expected_resource_version\x18\x02 \x01(\x04R\x17expectedResourceVersion\x12\x0e\n" + - "\x02id\x18\x03 \x01(\tR\x02id\x12\x1d\n" + - "\n" + - "request_id\x18\x05 \x01(\tR\trequestId\"\xbe\x01\n" + - "\x1eUpdateProviderProfilesResponse\x12I\n" + - "\vdiagnostics\x18\x01 \x03(\v2'.openshell.v1.ProviderProfileDiagnosticR\vdiagnostics\x127\n" + - "\aprofile\x18\x02 \x01(\v2\x1d.openshell.v1.ProviderProfileR\aprofile\x12\x18\n" + - "\aupdated\x18\x03 \x01(\bR\aupdated\"\xb6\x01\n" + - "\x1bLintProviderProfilesRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12C\n" + - "\bprofiles\x18\x01 \x03(\v2'.openshell.v1.ProviderProfileImportItemR\bprofiles\"\xbf\x01\n" + - "\x1cLintProviderProfilesResponse\x12I\n" + - "\vdiagnostics\x18\x01 \x03(\v2'.openshell.v1.ProviderProfileDiagnosticR\vdiagnostics\x12\x14\n" + - "\x05valid\x18\x02 \x01(\bR\x05valid\x12>\n" + - "\x1bsupports_stable_placeholder\x18\x03 \x01(\bR\x19supportsStablePlaceholder\"`\n" + - "\x16DeleteProviderResponse\x127\n" + - "\aoutcome\x18\x02 \x01(\x0e2\x1d.openshell.v1.DeletionOutcomeR\aoutcomeJ\x04\b\x01\x10\x02R\adeleted\"\xc6\x01\n" + - "\x1cDeleteProviderProfileRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12#\n" + - "\rallow_missing\x18\x03 \x01(\bR\fallowMissing\x12\x1d\n" + - "\n" + - "request_id\x18\x04 \x01(\tR\trequestId\"g\n" + - "\x1dDeleteProviderProfileResponse\x127\n" + - "\aoutcome\x18\x02 \x01(\x0e2\x1d.openshell.v1.DeletionOutcomeR\aoutcomeJ\x04\b\x01\x10\x02R\adeleted\"\xf4\x01\n" + - "$GetSandboxProviderEnvironmentRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12M\n" + - "#supports_static_credential_bindings\x18\x02 \x01(\bR supportsStaticCredentialBindings\x12^\n" + - ",supports_stable_placeholder_environment_keys\x18\x03 \x01(\bR(supportsStablePlaceholderEnvironmentKeys\"]\n" + - "\x1fStaticCredentialEndpointBinding\x12\x12\n" + - "\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\x02 \x01(\rR\x04port\x12\x12\n" + - "\x04path\x18\x03 \x01(\tR\x04path\"\xd5\x01\n" + - "\x17StaticCredentialBinding\x12K\n" + - "\tendpoints\x18\x01 \x03(\v2-.openshell.v1.StaticCredentialEndpointBindingR\tendpoints\x12/\n" + - "\x13credential_identity\x18\x02 \x01(\tR\x12credentialIdentity\x12<\n" + - "\x1aworkload_credential_handle\x18\x03 \x01(\tR\x18workloadCredentialHandle\"\xc9\f\n" + - "%GetSandboxProviderEnvironmentResponse\x12l\n" + - "\venvironment\x18\x01 \x03(\v2D.openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntryB\x04\x88\xb5\x18\x01R\venvironment\x122\n" + - "\x15provider_env_revision\x18\x02 \x01(\x04R\x13providerEnvRevision\x12\x92\x01\n" + - "\x1bcredential_expiration_times\x18g \x03(\v2R.openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntryR\x19credentialExpirationTimes\x12|\n" + - "\x13dynamic_credentials\x18\x04 \x03(\v2K.openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntryR\x12dynamicCredentials\x12\x8f\x01\n" + - "\x1astatic_credential_bindings\x18\x05 \x03(\v2Q.openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntryR\x18staticCredentialBindings\x12=\n" + - "\x1bnon_secret_environment_keys\x18\x06 \x03(\tR\x18nonSecretEnvironmentKeys\x12:\n" + - "\x19provider_attachment_epoch\x18\a \x01(\tR\x17providerAttachmentEpoch\x12\x1f\n" + - "\vpolicy_hash\x18\b \x01(\tR\n" + - "policyHash\x12P\n" + - "\x10readiness_reason\x18\t \x01(\x0e2%.openshell.v1.ProviderReadinessReasonR\x0freadinessReason\x12T\n" + - "\x05files\x18\n" + - " \x03(\v2>.openshell.v1.GetSandboxProviderEnvironmentResponse.FilesEntryR\x05files\x12M\n" + - "#stable_placeholder_environment_keys\x18\v \x03(\tR stablePlaceholderEnvironmentKeys\x12^\n" + - ",supports_stable_placeholder_environment_keys\x18\f \x01(\bR(supportsStablePlaceholderEnvironmentKeys\x1a>\n" + - "\x10EnvironmentEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1ah\n" + - "\x1eCredentialExpirationTimesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x120\n" + - "\x05value\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x05value:\x028\x01\x1an\n" + - "\x17DynamicCredentialsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12=\n" + - "\x05value\x18\x02 \x01(\v2'.openshell.v1.ProviderProfileCredentialR\x05value:\x028\x01\x1ar\n" + - "\x1dStaticCredentialBindingsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12;\n" + - "\x05value\x18\x02 \x01(\v2%.openshell.v1.StaticCredentialBindingR\x05value:\x028\x01\x1a8\n" + - "\n" + - "FilesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01J\x04\b\x03\x10\x04R\x18credential_expires_at_ms\"\xff\x02\n" + - "\x18ProviderEnvironmentValue\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12\x1a\n" + - "\x05value\x18\x02 \x01(\tB\x04\x88\xb5\x18\x01R\x05value\x12\\\n" + - "\x0eclassification\x18\x04 \x01(\x0e24.openshell.v1.ProviderEnvironmentValueClassificationR\x0eclassification\x12a\n" + - "\x19static_credential_binding\x18\x05 \x01(\v2%.openshell.v1.StaticCredentialBindingR\x17staticCredentialBinding\x12C\n" + - "\x0fexpiration_time\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x0eexpirationTime\x12-\n" + - "\x12stable_placeholder\x18\x06 \x01(\bR\x11stablePlaceholder\"\xaa\x05\n" + - "\x1bProviderEnvironmentSnapshot\x122\n" + - "\x15provider_env_revision\x18\x01 \x01(\x04R\x13providerEnvRevision\x12>\n" + - "\x06values\x18\x02 \x03(\v2&.openshell.v1.ProviderEnvironmentValueR\x06values\x12r\n" + - "\x13dynamic_credentials\x18\x03 \x03(\v2A.openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntryR\x12dynamicCredentials\x12:\n" + - "\x19provider_attachment_epoch\x18\x04 \x01(\tR\x17providerAttachmentEpoch\x12\x1f\n" + - "\vpolicy_hash\x18\x05 \x01(\tR\n" + - "policyHash\x12P\n" + - "\x10readiness_reason\x18\x06 \x01(\x0e2%.openshell.v1.ProviderReadinessReasonR\x0freadinessReason\x12J\n" + - "\x05files\x18\a \x03(\v24.openshell.v1.ProviderEnvironmentSnapshot.FilesEntryR\x05files\x1an\n" + - "\x17DynamicCredentialsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12=\n" + - "\x05value\x18\x02 \x01(\v2'.openshell.v1.ProviderProfileCredentialR\x05value:\x028\x01\x1a8\n" + - "\n" + - "FilesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xbd\x01\n" + - "#ExchangeProviderSubjectTokenRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x1a\n" + - "\bprovider\x18\x02 \x01(\tR\bprovider\x12%\n" + - "\x0ecredential_key\x18\x03 \x01(\tR\rcredentialKey\x124\n" + - "\x13supervisor_jwt_svid\x18\x04 \x01(\tB\x04\x88\xb5\x18\x01R\x11supervisorJwtSvid\"\xc0\x01\n" + - "$ExchangeProviderSubjectTokenResponse\x12'\n" + - "\faccess_token\x18\x01 \x01(\tB\x04\x88\xb5\x18\x01R\vaccessToken\x12>\n" + - "\rexpires_after\x18f \x01(\v2\x19.google.protobuf.DurationR\fexpiresAfter\x12\x1d\n" + - "\n" + - "token_type\x18\x03 \x01(\tR\ttokenTypeJ\x04\b\x02\x10\x03R\n" + - "expires_in\"\xa9\x05\n" + - "\x13UpdateConfigRequest\x12R\n" + - "\x0fworkspace_scope\x18\n" + - " \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12;\n" + - "\x06policy\x18\x02 \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x06policy\x12\x1f\n" + - "\vsetting_key\x18\x03 \x01(\tR\n" + - "settingKey\x12G\n" + - "\rsetting_value\x18\x04 \x01(\v2\".openshell.sandbox.v1.SettingValueR\fsettingValue\x12%\n" + - "\x0edelete_setting\x18\x05 \x01(\bR\rdeleteSetting\x12\x16\n" + - "\x06global\x18\x06 \x01(\bR\x06global\x12M\n" + - "\x10merge_operations\x18\a \x03(\v2\".openshell.v1.PolicyMergeOperationR\x0fmergeOperations\x12:\n" + - "\x19expected_resource_version\x18\b \x01(\x04R\x17expectedResourceVersion\x12T\n" + - "\vannotations\x18\t \x03(\v22.openshell.v1.UpdateConfigRequest.AnnotationsEntryR\vannotations\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1d\n" + - "\n" + - "request_id\x18\v \x01(\tR\trequestId\x1a>\n" + - "\x10AnnotationsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xc7\x03\n" + - "\x14PolicyMergeOperation\x129\n" + - "\badd_rule\x18\x01 \x01(\v2\x1c.openshell.v1.AddNetworkRuleH\x00R\aaddRule\x12N\n" + - "\x0fremove_endpoint\x18\x02 \x01(\v2#.openshell.v1.RemoveNetworkEndpointH\x00R\x0eremoveEndpoint\x12B\n" + - "\vremove_rule\x18\x03 \x01(\v2\x1f.openshell.v1.RemoveNetworkRuleH\x00R\n" + - "removeRule\x12B\n" + - "\x0eadd_deny_rules\x18\x04 \x01(\v2\x1a.openshell.v1.AddDenyRulesH\x00R\faddDenyRules\x12E\n" + - "\x0fadd_allow_rules\x18\x05 \x01(\v2\x1b.openshell.v1.AddAllowRulesH\x00R\raddAllowRules\x12H\n" + - "\rremove_binary\x18\x06 \x01(\v2!.openshell.v1.RemoveNetworkBinaryH\x00R\fremoveBinaryB\v\n" + - "\toperation\"j\n" + - "\x0eAddNetworkRule\x12\x1b\n" + - "\trule_name\x18\x01 \x01(\tR\bruleName\x12;\n" + - "\x04rule\x18\x02 \x01(\v2'.openshell.sandbox.v1.NetworkPolicyRuleR\x04rule\"\\\n" + - "\x15RemoveNetworkEndpoint\x12\x1b\n" + - "\trule_name\x18\x01 \x01(\tR\bruleName\x12\x12\n" + - "\x04host\x18\x02 \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\x03 \x01(\rR\x04port\"0\n" + - "\x11RemoveNetworkRule\x12\x1b\n" + - "\trule_name\x18\x01 \x01(\tR\bruleName\"\xd7\x01\n" + - "\fL7RuleTarget\x12\x1b\n" + - "\trule_name\x18\x01 \x01(\tR\bruleName\x12\x12\n" + - "\x04host\x18\x02 \x01(\tR\x04host\x12\x14\n" + - "\x05ports\x18\x03 \x03(\rR\x05ports\x12\x17\n" + - "\x04path\x18\x04 \x01(\tH\x00R\x04path\x88\x01\x01\x12?\n" + - "\bbinaries\x18\x05 \x03(\v2#.openshell.sandbox.v1.NetworkBinaryR\bbinaries\x12\x1d\n" + - "\n" + - "any_binary\x18\x06 \x01(\bR\tanyBinaryB\a\n" + - "\x05_path\"\x9b\x01\n" + - "\fAddDenyRules\x12?\n" + - "\n" + - "deny_rules\x18\x03 \x03(\v2 .openshell.sandbox.v1.L7DenyRuleR\tdenyRules\x122\n" + - "\x06target\x18\x04 \x01(\v2\x1a.openshell.v1.L7RuleTargetR\x06targetJ\x04\b\x01\x10\x02J\x04\b\x02\x10\x03R\x04hostR\x04port\"\x8f\x01\n" + - "\rAddAllowRules\x122\n" + - "\x05rules\x18\x03 \x03(\v2\x1c.openshell.sandbox.v1.L7RuleR\x05rules\x122\n" + - "\x06target\x18\x04 \x01(\v2\x1a.openshell.v1.L7RuleTargetR\x06targetJ\x04\b\x01\x10\x02J\x04\b\x02\x10\x03R\x04hostR\x04port\"S\n" + - "\x13RemoveNetworkBinary\x12\x1b\n" + - "\trule_name\x18\x01 \x01(\tR\bruleName\x12\x1f\n" + - "\vbinary_path\x18\x02 \x01(\tR\n" + - "binaryPath\"\xaf\x02\n" + - "\x14UpdateConfigResponse\x12\x18\n" + - "\aversion\x18\x01 \x01(\rR\aversion\x12\x1f\n" + - "\vpolicy_hash\x18\x02 \x01(\tR\n" + - "policyHash\x12+\n" + - "\x11settings_revision\x18\x03 \x01(\x04R\x10settingsRevision\x12\x18\n" + - "\adeleted\x18\x04 \x01(\bR\adeleted\x12U\n" + - "\vannotations\x18\x05 \x03(\v23.openshell.v1.UpdateConfigResponse.AnnotationsEntryR\vannotations\x1a>\n" + - "\x10AnnotationsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xbf\x01\n" + - "\x1dGetSandboxPolicyStatusRequest\x12R\n" + - "\x0fworkspace_scope\x18\x04 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x18\n" + - "\aversion\x18\x02 \x01(\rR\aversion\x12\x16\n" + - "\x06global\x18\x03 \x01(\bR\x06global\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\"\x88\x01\n" + - "\x1eGetSandboxPolicyStatusResponse\x12?\n" + - "\brevision\x18\x01 \x01(\v2#.openshell.v1.SandboxPolicyRevisionR\brevision\x12%\n" + - "\x0eactive_version\x18\x02 \x01(\rR\ractiveVersion\"\xde\x01\n" + - "\x1aListSandboxPoliciesRequest\x12R\n" + - "\x0fworkspace_scope\x18\x05 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x1b\n" + - "\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12\x1d\n" + - "\n" + - "page_token\x18\x03 \x01(\tR\tpageToken\x12\x16\n" + - "\x06global\x18\x04 \x01(\bR\x06global\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\"\x88\x01\n" + - "\x1bListSandboxPoliciesResponse\x12A\n" + - "\trevisions\x18\x01 \x03(\v2#.openshell.v1.SandboxPolicyRevisionR\trevisions\x12&\n" + - "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\xa7\x01\n" + - "\x19ReportPolicyStatusRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x18\n" + - "\aversion\x18\x02 \x01(\rR\aversion\x122\n" + - "\x06status\x18\x03 \x01(\x0e2\x1a.openshell.v1.PolicyStatusR\x06status\x12\x1d\n" + - "\n" + - "load_error\x18\x04 \x01(\tR\tloadError\"\x1c\n" + - "\x1aReportPolicyStatusResponse\"\xbc\x02\n" + - "\x1dSandboxConfigurationAdmission\x12\x1f\n" + - "\vinstance_id\x18\x01 \x01(\tR\n" + - "instanceId\x12?\n" + - "\x05state\x18\x02 \x01(\x0e2).openshell.v1.ConfigurationAdmissionStateR\x05state\x12%\n" + - "\x0epolicy_version\x18\x03 \x01(\rR\rpolicyVersion\x12\x1f\n" + - "\vpolicy_hash\x18\x04 \x01(\tR\n" + - "policyHash\x12'\n" + - "\x0fconfig_revision\x18\x05 \x01(\x04R\x0econfigRevision\x122\n" + - "\x15provider_env_revision\x18\x06 \x01(\x04R\x13providerEnvRevision\x12\x14\n" + - "\x05error\x18\a \x01(\tR\x05error\"\xbf\x01\n" + - "!ReportSandboxConfigurationRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12I\n" + - "\tadmission\x18\x02 \x01(\v2+.openshell.v1.SandboxConfigurationAdmissionR\tadmission\x120\n" + - "\x14expected_instance_id\x18\x03 \x01(\tR\x12expectedInstanceId\"$\n" + - "\"ReportSandboxConfigurationResponse\"\x9b\x04\n" + - "\x15SandboxPolicyRevision\x12\x18\n" + - "\aversion\x18\x01 \x01(\rR\aversion\x12\x1f\n" + - "\vpolicy_hash\x18\x02 \x01(\tR\n" + - "policyHash\x122\n" + - "\x06status\x18\x03 \x01(\x0e2\x1a.openshell.v1.PolicyStatusR\x06status\x12\x1d\n" + - "\n" + - "load_error\x18\x04 \x01(\tR\tloadError\x12=\n" + - "\fcreated_time\x18i \x01(\v2\x1a.google.protobuf.TimestampR\vcreatedTime\x12;\n" + - "\vloaded_time\x18j \x01(\v2\x1a.google.protobuf.TimestampR\n" + - "loadedTime\x12;\n" + - "\x06policy\x18\a \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x06policy\x12S\n" + - "\n" + - "provenance\x18\b \x03(\v23.openshell.v1.SandboxPolicyRevision.ProvenanceEntryR\n" + - "provenance\x1a=\n" + - "\x0fProvenanceEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01J\x04\b\x05\x10\x06J\x04\b\x06\x10\aR\rcreated_at_msR\floaded_at_ms\"\x9d\x02\n" + - "\x15GetSandboxLogsRequest\x12R\n" + - "\x0fworkspace_scope\x18\x06 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x14\n" + - "\x05lines\x18\x02 \x01(\rR\x05lines\x129\n" + - "\n" + - "since_time\x18g \x01(\v2\x1a.google.protobuf.TimestampR\tsinceTime\x12\x18\n" + - "\asources\x18\x04 \x03(\tR\asources\x12\x1b\n" + - "\tmin_level\x18\x05 \x01(\tR\bminLevelJ\x04\b\x03\x10\x04R\bsince_ms\"i\n" + - "\x16PushSandboxLogsRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x120\n" + - "\x04logs\x18\x02 \x03(\v2\x1c.openshell.v1.SandboxLogLineR\x04logs\"\x19\n" + - "\x17PushSandboxLogsResponse\"m\n" + - "\x16GetSandboxLogsResponse\x120\n" + - "\x04logs\x18\x01 \x03(\v2\x1c.openshell.v1.SandboxLogLineR\x04logs\x12!\n" + - "\fbuffer_total\x18\x02 \x01(\rR\vbufferTotal\"\xf8\x02\n" + - "\x11SupervisorMessage\x125\n" + - "\x05hello\x18\x01 \x01(\v2\x1d.openshell.v1.SupervisorHelloH\x00R\x05hello\x12A\n" + - "\theartbeat\x18\x02 \x01(\v2!.openshell.v1.SupervisorHeartbeatH\x00R\theartbeat\x12K\n" + - "\x11relay_open_result\x18\x03 \x01(\v2\x1d.openshell.v1.RelayOpenResultH\x00R\x0frelayOpenResult\x12;\n" + - "\vrelay_close\x18\x04 \x01(\v2\x18.openshell.v1.RelayCloseH\x00R\n" + - "relayClose\x12T\n" + - "\x14config_update_result\x18\x05 \x01(\v2 .openshell.v1.ConfigUpdateResultH\x00R\x12configUpdateResultB\t\n" + - "\apayload\"\xf9\x03\n" + - "\x0eGatewayMessage\x12J\n" + - "\x10session_accepted\x18\x01 \x01(\v2\x1d.openshell.v1.SessionAcceptedH\x00R\x0fsessionAccepted\x12J\n" + - "\x10session_rejected\x18\x02 \x01(\v2\x1d.openshell.v1.SessionRejectedH\x00R\x0fsessionRejected\x12>\n" + - "\theartbeat\x18\x03 \x01(\v2\x1e.openshell.v1.GatewayHeartbeatH\x00R\theartbeat\x128\n" + - "\n" + - "relay_open\x18\x04 \x01(\v2\x17.openshell.v1.RelayOpenH\x00R\trelayOpen\x12;\n" + - "\vrelay_close\x18\x05 \x01(\v2\x18.openshell.v1.RelayCloseH\x00R\n" + - "relayClose\x12J\n" + - "\x10session_redirect\x18\x06 \x01(\v2\x1d.openshell.v1.SessionRedirectH\x00R\x0fsessionRedirect\x12A\n" + - "\rconfig_update\x18\a \x01(\v2\x1a.openshell.v1.ConfigUpdateH\x00R\fconfigUpdateB\t\n" + - "\apayload\"\xca\x02\n" + - "\x0fSupervisorHello\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x1f\n" + - "\vinstance_id\x18\x02 \x01(\tR\n" + - "instanceId\x12)\n" + - "\x10connection_epoch\x18\x03 \x01(\x04R\x0fconnectionEpoch\x12>\n" + - "\x1bsupports_provider_readiness\x18\x04 \x01(\bR\x19supportsProviderReadiness\x12\x1e\n" + - "\n" + - "redirected\x18\x05 \x01(\bR\n" + - "redirected\x12:\n" + - "\x19supports_session_redirect\x18\x06 \x01(\bR\x17supportsSessionRedirect\x120\n" + - "\x14supports_config_push\x18\a \x01(\bR\x12supportsConfigPush\"\x83\x02\n" + - "\x0fSessionAccepted\x12\x1d\n" + - "\n" + - "session_id\x18\x01 \x01(\tR\tsessionId\x128\n" + - "\tbootstrap\x18\x03 \x01(\v2\x1a.openshell.v1.ConfigUpdateR\tbootstrap\x12.\n" + - "\x13config_push_enabled\x18\x04 \x01(\bR\x11configPushEnabled\x12H\n" + - "\x12heartbeat_interval\x18f \x01(\v2\x19.google.protobuf.DurationR\x11heartbeatIntervalJ\x04\b\x02\x10\x03R\x17heartbeat_interval_secs\"\xdd\x01\n" + - "\fConfigUpdate\x12\x1b\n" + - "\tupdate_id\x18\x01 \x01(\tR\bupdateId\x12R\n" + - "\x0esandbox_config\x18\x02 \x01(\v2+.openshell.sandbox.v1.SandboxConfigSnapshotR\rsandboxConfig\x12\\\n" + - "\x14provider_environment\x18\x03 \x01(\v2).openshell.v1.ProviderEnvironmentSnapshotR\x13providerEnvironment\"`\n" + - "\x12ConfigApplyFailure\x12\x12\n" + - "\x04code\x18\x01 \x01(\tR\x04code\x12\x18\n" + - "\amessage\x18\x02 \x01(\tR\amessage\x12\x1c\n" + - "\tretryable\x18\x03 \x01(\bR\tretryable\"\xb5\x02\n" + - "\x12ConfigUpdateResult\x12\x1b\n" + - "\tupdate_id\x18\x01 \x01(\tR\bupdateId\x12:\n" + - "\aoutcome\x18\x02 \x01(\x0e2 .openshell.v1.ConfigApplyOutcomeR\aoutcome\x12:\n" + - "\afailure\x18\x03 \x01(\v2 .openshell.v1.ConfigApplyFailureR\afailure\x12I\n" + - "\tadmission\x18\x04 \x01(\v2+.openshell.v1.SandboxConfigurationAdmissionR\tadmission\x12?\n" + - "\vpolicy_load\x18\x05 \x01(\v2\x1e.openshell.v1.PolicyLoadResultR\n" + - "policyLoad\"Z\n" + - "\x10PolicyLoadResult\x12\x18\n" + - "\aversion\x18\x01 \x01(\rR\aversion\x12\x16\n" + - "\x06loaded\x18\x02 \x01(\bR\x06loaded\x12\x14\n" + - "\x05error\x18\x03 \x01(\tR\x05error\")\n" + - "\x0fSessionRejected\x12\x16\n" + - "\x06reason\x18\x01 \x01(\tR\x06reason\"`\n" + - "\x0fSessionRedirect\x12#\n" + - "\rpeer_endpoint\x18\x01 \x01(\tR\fpeerEndpoint\x12(\n" + - "\x10owner_replica_id\x18\x02 \x01(\tR\x0eownerReplicaId\"\x15\n" + - "\x13SupervisorHeartbeat\"\x12\n" + - "\x10GatewayHeartbeat\"{\n" + - "\x1cReportMainProcessExitRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x1f\n" + - "\vinstance_id\x18\x02 \x01(\tR\n" + - "instanceId\x12\x1b\n" + - "\texit_code\x18\x03 \x01(\x05R\bexitCode\"\x1f\n" + - "\x1dReportMainProcessExitResponse\"`\n" + - "\x1eFinalizeMainProcessExitRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x1f\n" + - "\vinstance_id\x18\x02 \x01(\tR\n" + - "instanceId\"!\n" + - "\x1fFinalizeMainProcessExitResponse\"\xb7\x01\n" + - "\tRelayOpen\x12\x1d\n" + - "\n" + - "channel_id\x18\x01 \x01(\tR\tchannelId\x120\n" + - "\x03ssh\x18\x02 \x01(\v2\x1c.openshell.v1.SshRelayTargetH\x00R\x03ssh\x120\n" + - "\x03tcp\x18\x03 \x01(\v2\x1c.openshell.v1.TcpRelayTargetH\x00R\x03tcp\x12\x1d\n" + - "\n" + - "service_id\x18\x05 \x01(\tR\tserviceIdB\b\n" + - "\x06target\"\x10\n" + - "\x0eSshRelayTarget\"8\n" + - "\x0eTcpRelayTarget\x12\x12\n" + - "\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\x02 \x01(\rR\x04port\"*\n" + - "\tRelayInit\x12\x1d\n" + - "\n" + - "channel_id\x18\x01 \x01(\tR\tchannelId\"\\\n" + - "\n" + - "RelayFrame\x12-\n" + - "\x04init\x18\x01 \x01(\v2\x17.openshell.v1.RelayInitH\x00R\x04init\x12\x14\n" + - "\x04data\x18\x02 \x01(\fH\x00R\x04dataB\t\n" + - "\apayload\"\x98\x01\n" + - "\rPeerRelayInit\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x126\n" + - "\n" + - "relay_open\x18\x02 \x01(\v2\x17.openshell.v1.RelayOpenR\trelayOpen\x120\n" + - "\x14requester_replica_id\x18\x03 \x01(\tR\x12requesterReplicaId\"d\n" + - "\x0ePeerRelayFrame\x121\n" + - "\x04init\x18\x01 \x01(\v2\x1b.openshell.v1.PeerRelayInitH\x00R\x04init\x12\x14\n" + - "\x04data\x18\x02 \x01(\fH\x00R\x04dataB\t\n" + - "\apayload\"W\n" + - "\x17PeerConfigSandboxTarget\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x1d\n" + - "\n" + - "session_id\x18\x02 \x01(\tR\tsessionId\"L\n" + - "\x18PeerConfigProviderTarget\x12\x1c\n" + - "\tworkspace\x18\x01 \x01(\tR\tworkspace\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\"\xf8\x01\n" + - "\x1dPeerNotifyConfigUpdateRequest\x12A\n" + - "\asandbox\x18\x01 \x01(\v2%.openshell.v1.PeerConfigSandboxTargetH\x00R\asandbox\x12\x1e\n" + - "\tworkspace\x18\x02 \x01(\tH\x00R\tworkspace\x12%\n" + - "\rall_connected\x18\x03 \x01(\bH\x00R\fallConnected\x12D\n" + - "\bprovider\x18\x04 \x01(\v2&.openshell.v1.PeerConfigProviderTargetH\x00R\bproviderB\a\n" + - "\x05scope\"A\n" + - "\x1ePeerNotifyConfigUpdateResponse\x12\x1f\n" + - "\vstale_owner\x18\x01 \x01(\bR\n" + - "staleOwner\"`\n" + - "\x0fRelayOpenResult\x12\x1d\n" + - "\n" + - "channel_id\x18\x01 \x01(\tR\tchannelId\x12\x18\n" + - "\asuccess\x18\x02 \x01(\bR\asuccess\x12\x14\n" + - "\x05error\x18\x03 \x01(\tR\x05error\"C\n" + - "\n" + - "RelayClose\x12\x1d\n" + - "\n" + - "channel_id\x18\x01 \x01(\tR\tchannelId\x12\x16\n" + - "\x06reason\x18\x02 \x01(\tR\x06reason\"o\n" + - "\x0fL7RequestSample\x12\x16\n" + - "\x06method\x18\x01 \x01(\tR\x06method\x12\x12\n" + - "\x04path\x18\x02 \x01(\tR\x04path\x12\x1a\n" + - "\bdecision\x18\x03 \x01(\tR\bdecision\x12\x14\n" + - "\x05count\x18\x04 \x01(\rR\x05count\"\xce\x05\n" + - "\rDenialSummary\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x12\n" + - "\x04host\x18\x02 \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\x03 \x01(\rR\x04port\x12\x16\n" + - "\x06binary\x18\x04 \x01(\tR\x06binary\x12\x1c\n" + - "\tancestors\x18\x05 \x03(\tR\tancestors\x12\x1f\n" + - "\vdeny_reason\x18\x06 \x01(\tR\n" + - "denyReason\x12B\n" + - "\x0ffirst_seen_time\x18k \x01(\v2\x1a.google.protobuf.TimestampR\rfirstSeenTime\x12@\n" + - "\x0elast_seen_time\x18l \x01(\v2\x1a.google.protobuf.TimestampR\flastSeenTime\x12\x14\n" + - "\x05count\x18\t \x01(\rR\x05count\x12)\n" + - "\x10suppressed_count\x18\n" + - " \x01(\rR\x0fsuppressedCount\x12\x1f\n" + - "\vtotal_count\x18\v \x01(\rR\n" + - "totalCount\x12'\n" + - "\x0fsample_cmdlines\x18\f \x03(\tR\x0esampleCmdlines\x12#\n" + - "\rbinary_sha256\x18\r \x01(\tR\fbinarySha256\x12\x1e\n" + - "\n" + - "persistent\x18\x0e \x01(\bR\n" + - "persistent\x12!\n" + - "\fdenial_stage\x18\x0f \x01(\tR\vdenialStage\x12K\n" + - "\x12l7_request_samples\x18\x10 \x03(\v2\x1d.openshell.v1.L7RequestSampleR\x10l7RequestSamples\x120\n" + - "\x14l7_inspection_active\x18\x11 \x01(\bR\x12l7InspectionActiveJ\x04\b\a\x10\bJ\x04\b\b\x10\tR\rfirst_seen_msR\flast_seen_ms\"T\n" + - "\x10DenialGroupCount\x12\x1d\n" + - "\n" + - "deny_group\x18\x01 \x01(\tR\tdenyGroup\x12!\n" + - "\fdenied_count\x18\x02 \x01(\rR\vdeniedCount\"\xc8\x01\n" + - "\x16NetworkActivitySummary\x124\n" + - "\x16network_activity_count\x18\x01 \x01(\rR\x14networkActivityCount\x12.\n" + - "\x13denied_action_count\x18\x02 \x01(\rR\x11deniedActionCount\x12H\n" + - "\x10denials_by_group\x18\x03 \x03(\v2\x1e.openshell.v1.DenialGroupCountR\x0edenialsByGroup\"\xf9\t\n" + - "\vPolicyChunk\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n" + - "\x06status\x18\x02 \x01(\tR\x06status\x12\x1b\n" + - "\trule_name\x18\x03 \x01(\tR\bruleName\x12L\n" + - "\rproposed_rule\x18\x04 \x01(\v2'.openshell.sandbox.v1.NetworkPolicyRuleR\fproposedRule\x12\x1c\n" + - "\trationale\x18\x05 \x01(\tR\trationale\x12%\n" + - "\x0esecurity_notes\x18\x06 \x01(\tR\rsecurityNotes\x12\x1e\n" + - "\n" + - "confidence\x18\a \x01(\x02R\n" + - "confidence\x12,\n" + - "\x12denial_summary_ids\x18\b \x03(\tR\x10denialSummaryIds\x12=\n" + - "\fcreated_time\x18m \x01(\v2\x1a.google.protobuf.TimestampR\vcreatedTime\x12=\n" + - "\fdecided_time\x18n \x01(\v2\x1a.google.protobuf.TimestampR\vdecidedTime\x12\x14\n" + - "\x05stage\x18\v \x01(\tR\x05stage\x12.\n" + - "\x13supersedes_chunk_id\x18\f \x01(\tR\x11supersedesChunkId\x12\x1b\n" + - "\thit_count\x18\r \x01(\x05R\bhitCount\x12B\n" + - "\x0ffirst_seen_time\x18r \x01(\v2\x1a.google.protobuf.TimestampR\rfirstSeenTime\x12@\n" + - "\x0elast_seen_time\x18s \x01(\v2\x1a.google.protobuf.TimestampR\flastSeenTime\x12\x16\n" + - "\x06binary\x18\x10 \x01(\tR\x06binary\x12+\n" + - "\x11validation_result\x18\x11 \x01(\tR\x10validationResult\x12)\n" + - "\x10rejection_reason\x18\x12 \x01(\tR\x0frejectionReason\x12+\n" + - "\x11application_error\x18\x13 \x01(\tR\x10applicationError\x12!\n" + - "\freview_token\x18\x14 \x01(\tR\vreviewToken\x12A\n" + - "\x1dcurrent_effective_policy_hash\x18\x15 \x01(\tR\x1acurrentEffectivePolicyHash\x12E\n" + - "\x1fcandidate_effective_policy_hash\x18\x16 \x01(\tR\x1ccandidateEffectivePolicyHash\x12]\n" + - "\x18current_effective_policy\x18\x17 \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x16currentEffectivePolicy\x12a\n" + - "\x1acandidate_effective_policy\x18\x18 \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x18candidateEffectivePolicyJ\x04\b\t\x10\n" + - "J\x04\b\n" + - "\x10\vJ\x04\b\x0e\x10\x0fJ\x04\b\x0f\x10\x10R\rcreated_at_msR\rdecided_at_msR\rfirst_seen_msR\flast_seen_ms\"\x96\x01\n" + - "\x11DraftPolicyUpdate\x12#\n" + - "\rdraft_version\x18\x01 \x01(\x04R\fdraftVersion\x12\x1d\n" + - "\n" + - "new_chunks\x18\x02 \x01(\rR\tnewChunks\x12#\n" + - "\rtotal_pending\x18\x03 \x01(\rR\ftotalPending\x12\x18\n" + - "\asummary\x18\x04 \x01(\tR\asummary\"\x8d\x03\n" + - "\x1bSubmitPolicyAnalysisRequest\x12R\n" + - "\x0fworkspace_scope\x18\x06 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x129\n" + - "\tsummaries\x18\x01 \x03(\v2\x1b.openshell.v1.DenialSummaryR\tsummaries\x12B\n" + - "\x0fproposed_chunks\x18\x02 \x03(\v2\x19.openshell.v1.PolicyChunkR\x0eproposedChunks\x12#\n" + - "\ranalysis_mode\x18\x03 \x01(\tR\fanalysisMode\x12\x12\n" + - "\x04name\x18\x04 \x01(\tR\x04name\x12b\n" + - "\x1anetwork_activity_summaries\x18\x05 \x03(\v2$.openshell.v1.NetworkActivitySummaryR\x18networkActivitySummaries\"\xcb\x01\n" + - "\x1cSubmitPolicyAnalysisResponse\x12'\n" + - "\x0faccepted_chunks\x18\x01 \x01(\rR\x0eacceptedChunks\x12'\n" + - "\x0frejected_chunks\x18\x02 \x01(\rR\x0erejectedChunks\x12+\n" + - "\x11rejection_reasons\x18\x03 \x03(\tR\x10rejectionReasons\x12,\n" + - "\x12accepted_chunk_ids\x18\x04 \x03(\tR\x10acceptedChunkIds\"\xaa\x01\n" + - "\x15GetDraftPolicyRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12#\n" + - "\rstatus_filter\x18\x02 \x01(\tR\fstatusFilter\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\"\xfe\x01\n" + - "\x16GetDraftPolicyResponse\x121\n" + - "\x06chunks\x18\x01 \x03(\v2\x19.openshell.v1.PolicyChunkR\x06chunks\x12'\n" + - "\x0frolling_summary\x18\x02 \x01(\tR\x0erollingSummary\x12#\n" + - "\rdraft_version\x18\x03 \x01(\x04R\fdraftVersion\x12H\n" + - "\x12last_analyzed_time\x18h \x01(\v2\x1a.google.protobuf.TimestampR\x10lastAnalyzedTimeJ\x04\b\x04\x10\x05R\x13last_analyzed_at_ms\"\xe5\x01\n" + - "\x18ApproveDraftChunkRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x19\n" + - "\bchunk_id\x18\x02 \x01(\tR\achunkId\x12!\n" + - "\freview_token\x18\x04 \x01(\tR\vreviewToken\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1d\n" + - "\n" + - "request_id\x18\x05 \x01(\tR\trequestId\"c\n" + - "\x19ApproveDraftChunkResponse\x12%\n" + - "\x0epolicy_version\x18\x01 \x01(\rR\rpolicyVersion\x12\x1f\n" + - "\vpolicy_hash\x18\x02 \x01(\tR\n" + - "policyHash\"\xd9\x01\n" + - "\x17RejectDraftChunkRequest\x12R\n" + - "\x0fworkspace_scope\x18\x04 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x19\n" + - "\bchunk_id\x18\x02 \x01(\tR\achunkId\x12\x16\n" + - "\x06reason\x18\x03 \x01(\tR\x06reason\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1d\n" + - "\n" + - "request_id\x18\x05 \x01(\tR\trequestId\"\x1a\n" + - "\x18RejectDraftChunkResponse\"R\n" + - "\x12DraftChunkApproval\x12\x19\n" + - "\bchunk_id\x18\x01 \x01(\tR\achunkId\x12!\n" + - "\freview_token\x18\x02 \x01(\tR\vreviewToken\"\xa5\x02\n" + - "\x1cApproveAllDraftChunksRequest\x12R\n" + - "\x0fworkspace_scope\x18\x04 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x128\n" + - "\x18include_security_flagged\x18\x02 \x01(\bR\x16includeSecurityFlagged\x12>\n" + - "\tapprovals\x18\x03 \x03(\v2 .openshell.v1.DraftChunkApprovalR\tapprovals\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1d\n" + - "\n" + - "request_id\x18\x05 \x01(\tR\trequestId\"\xb7\x01\n" + - "\x1dApproveAllDraftChunksResponse\x12%\n" + - "\x0epolicy_version\x18\x01 \x01(\rR\rpolicyVersion\x12\x1f\n" + - "\vpolicy_hash\x18\x02 \x01(\tR\n" + - "policyHash\x12'\n" + - "\x0fchunks_approved\x18\x03 \x01(\rR\x0echunksApproved\x12%\n" + - "\x0echunks_skipped\x18\x04 \x01(\rR\rchunksSkipped\"\x8d\x02\n" + - "\x15EditDraftChunkRequest\x12R\n" + - "\x0fworkspace_scope\x18\x04 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x19\n" + - "\bchunk_id\x18\x02 \x01(\tR\achunkId\x12L\n" + - "\rproposed_rule\x18\x03 \x01(\v2'.openshell.sandbox.v1.NetworkPolicyRuleR\fproposedRule\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1d\n" + - "\n" + - "request_id\x18\x05 \x01(\tR\trequestId\"\x18\n" + - "\x16EditDraftChunkResponse\"\xbf\x01\n" + - "\x15UndoDraftChunkRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x19\n" + - "\bchunk_id\x18\x02 \x01(\tR\achunkId\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1d\n" + - "\n" + - "request_id\x18\x04 \x01(\tR\trequestId\"`\n" + - "\x16UndoDraftChunkResponse\x12%\n" + - "\x0epolicy_version\x18\x01 \x01(\rR\rpolicyVersion\x12\x1f\n" + - "\vpolicy_hash\x18\x02 \x01(\tR\n" + - "policyHash\"\xa6\x01\n" + - "\x17ClearDraftChunksRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x1d\n" + - "\n" + - "request_id\x18\x03 \x01(\tR\trequestId\"A\n" + - "\x18ClearDraftChunksResponse\x12%\n" + - "\x0echunks_cleared\x18\x01 \x01(\rR\rchunksCleared\"\x86\x01\n" + - "\x16GetDraftHistoryRequest\x12R\n" + - "\x0fworkspace_scope\x18\x02 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\"\xbe\x01\n" + - "\x11DraftHistoryEntry\x129\n" + - "\n" + - "event_time\x18e \x01(\v2\x1a.google.protobuf.TimestampR\teventTime\x12\x1d\n" + - "\n" + - "event_type\x18\x02 \x01(\tR\teventType\x12 \n" + - "\vdescription\x18\x03 \x01(\tR\vdescription\x12\x19\n" + - "\bchunk_id\x18\x04 \x01(\tR\achunkIdJ\x04\b\x01\x10\x02R\ftimestamp_ms\"T\n" + - "\x17GetDraftHistoryResponse\x129\n" + - "\aentries\x18\x01 \x03(\v2\x1f.openshell.v1.DraftHistoryEntryR\aentries\"\xd0\x01\n" + - "\x16CreateWorkspaceRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12H\n" + - "\x06labels\x18\x02 \x03(\v20.openshell.v1.CreateWorkspaceRequest.LabelsEntryR\x06labels\x12\x1d\n" + - "\n" + - "request_id\x18\x03 \x01(\tR\trequestId\x1a9\n" + - "\vLabelsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"Z\n" + - "\x17CreateWorkspaceResponse\x12?\n" + - "\tworkspace\x18\x01 \x01(\v2!.openshell.datamodel.v1.WorkspaceR\tworkspace\")\n" + - "\x13GetWorkspaceRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"W\n" + - "\x14GetWorkspaceResponse\x12?\n" + - "\tworkspace\x18\x01 \x01(\v2!.openshell.datamodel.v1.WorkspaceR\tworkspace\"z\n" + - "\x15ListWorkspacesRequest\x12\x1b\n" + - "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" + - "\n" + - "page_token\x18\x02 \x01(\tR\tpageToken\x12%\n" + - "\x0elabel_selector\x18\x03 \x01(\tR\rlabelSelector\"\x83\x01\n" + - "\x16ListWorkspacesResponse\x12A\n" + - "\n" + - "workspaces\x18\x01 \x03(\v2!.openshell.datamodel.v1.WorkspaceR\n" + - "workspaces\x12&\n" + - "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"p\n" + - "\x16DeleteWorkspaceRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12#\n" + - "\rallow_missing\x18\x02 \x01(\bR\fallowMissing\x12\x1d\n" + - "\n" + - "request_id\x18\x03 \x01(\tR\trequestId\"a\n" + - "\x17DeleteWorkspaceResponse\x127\n" + - "\aoutcome\x18\x02 \x01(\x0e2\x1d.openshell.v1.DeletionOutcomeR\aoutcomeJ\x04\b\x01\x10\x02R\adeleted\"\xaf\x01\n" + - "\x0fWorkspaceMember\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12+\n" + - "\x11principal_subject\x18\x02 \x01(\tR\x10principalSubject\x12/\n" + - "\x04role\x18\x03 \x01(\x0e2\x1b.openshell.v1.WorkspaceRoleR\x04role\"\xec\x01\n" + - "\x19AddWorkspaceMemberRequest\x12R\n" + - "\x0fworkspace_scope\x18\x01 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12+\n" + - "\x11principal_subject\x18\x02 \x01(\tR\x10principalSubject\x12/\n" + - "\x04role\x18\x03 \x01(\x0e2\x1b.openshell.v1.WorkspaceRoleR\x04role\x12\x1d\n" + - "\n" + - "request_id\x18\x04 \x01(\tR\trequestId\"S\n" + - "\x1aAddWorkspaceMemberResponse\x125\n" + - "\x06member\x18\x01 \x01(\v2\x1d.openshell.v1.WorkspaceMemberR\x06member\"\xe3\x01\n" + - "\x1cRemoveWorkspaceMemberRequest\x12R\n" + - "\x0fworkspace_scope\x18\x01 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12+\n" + - "\x11principal_subject\x18\x02 \x01(\tR\x10principalSubject\x12#\n" + - "\rallow_missing\x18\x03 \x01(\bR\fallowMissing\x12\x1d\n" + - "\n" + - "request_id\x18\x04 \x01(\tR\trequestId\"g\n" + - "\x1dRemoveWorkspaceMemberResponse\x127\n" + - "\aoutcome\x18\x02 \x01(\x0e2\x1d.openshell.v1.DeletionOutcomeR\aoutcomeJ\x04\b\x01\x10\x02R\aremoved\"\xad\x01\n" + - "\x1bListWorkspaceMembersRequest\x12R\n" + - "\x0fworkspace_scope\x18\x01 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x1b\n" + - "\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12\x1d\n" + - "\n" + - "page_token\x18\x03 \x01(\tR\tpageToken\"\x7f\n" + - "\x1cListWorkspaceMembersResponse\x127\n" + - "\amembers\x18\x01 \x03(\v2\x1d.openshell.v1.WorkspaceMemberR\amembers\x12&\n" + - "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\xb5\x01\n" + - "\x1aExtensionServiceCredential\x12!\n" + - "\fservice_name\x18\x01 \x01(\tR\vserviceName\x12\x1a\n" + - "\x05token\x18\x02 \x01(\tB\x04\x88\xb5\x18\x01R\x05token\x12C\n" + - "\x0fexpiration_time\x18g \x01(\v2\x1a.google.protobuf.TimestampR\x0eexpirationTimeJ\x04\b\x03\x10\x04R\rexpires_at_ms\"\xa5\x01\n" + - "\x13EndpointObservation\x12\x1f\n" + - "\vendpoint_id\x18\x01 \x01(\tR\n" + - "endpointId\x124\n" + - "\x06result\x18\x02 \x01(\x0e2\x1c.openshell.v1.EndpointResultR\x06result\x127\n" + - "\afailure\x18\x03 \x01(\v2\x1d.openshell.v1.EndpointFailureR\afailure\"\xb7\x03\n" + - "\x1bReportEndpointStatusRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x1f\n" + - "\vpolicy_hash\x18\x02 \x01(\tR\n" + - "policyHash\x122\n" + - "\x15provider_env_revision\x18\x03 \x01(\x04R\x13providerEnvRevision\x12E\n" + - "\fobservations\x18\x04 \x03(\v2!.openshell.v1.EndpointObservationR\fobservations\x122\n" + - "\x15observed_endpoint_ids\x18\x05 \x03(\tR\x13observedEndpointIds\x122\n" + - "\x15supervisor_session_id\x18\x06 \x01(\tR\x13supervisorSessionId\x12'\n" + - "\x0freport_sequence\x18\a \x01(\x04R\x0ereportSequence\x12L\n" + - "\x12token_grant_status\x18\b \x01(\v2\x1e.openshell.v1.TokenGrantStatusR\x10tokenGrantStatus\"a\n" + - "\x10TokenGrantStatus\x12M\n" + - "\flast_failure\x18\x01 \x01(\v2*.openshell.v1.TokenGrantFailureObservationR\vlastFailure\"\xad\x01\n" + - "\x1cTokenGrantFailureObservation\x12)\n" + - "\x10destination_host\x18\x01 \x01(\tR\x0fdestinationHost\x12)\n" + - "\x10destination_port\x18\x02 \x01(\rR\x0fdestinationPort\x127\n" + - "\afailure\x18\x03 \x01(\v2\x1d.openshell.v1.EndpointFailureR\afailure\"\x1e\n" + - "\x1cReportEndpointStatusResponse\"\xd2\x02\n" + - "\x0eEndpointStatus\x12\x1f\n" + - "\vendpoint_id\x18\x01 \x01(\tR\n" + - "endpointId\x12\x12\n" + - "\x04host\x18\x02 \x01(\tR\x04host\x12\x14\n" + - "\x05ports\x18\x03 \x03(\rR\x05ports\x12\x12\n" + - "\x04path\x18\x04 \x01(\tR\x04path\x12=\n" + - "\vlast_result\x18\x05 \x01(\x0e2\x1c.openshell.v1.EndpointResultR\n" + - "lastResult\x12H\n" + - "\x12last_reported_time\x18j \x01(\v2\x1a.google.protobuf.TimestampR\x10lastReportedTime\x12@\n" + - "\flast_failure\x18\a \x01(\v2\x1d.openshell.v1.EndpointFailureR\vlastFailureJ\x04\b\x06\x10\aR\x10last_reported_at\"\xd5\a\n" + - "\x13SandboxProvisioning\x12\x1d\n" + - "\n" + - "attempt_id\x18\x01 \x01(\tR\tattemptId\x126\n" + - "\x17configuration_change_id\x18\x02 \x01(\tR\x15configurationChangeId\x12V\n" + - "\x19configuration_change_time\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x17configurationChangeTime\x12L\n" + - "\x14first_rejection_time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\x12firstRejectionTime\x126\n" + - "\bdeadline\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\bdeadline\x12=\n" + - "\ftimeout_time\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\vtimeoutTime\x12P\n" + - "\x16cleanup_completed_time\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\x14cleanupCompletedTime\x12#\n" + - "\rcleanup_error\x18\b \x01(\tR\fcleanupError\x12H\n" + - "\x12cleanup_retry_time\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\x10cleanupRetryTime\x120\n" + - "\x14attachment_change_id\x18\n" + - " \x01(\tR\x12attachmentChangeId\x12P\n" + - "\x16attachment_change_time\x18\v \x01(\v2\x1a.google.protobuf.TimestampR\x14attachmentChangeTime\x12M\n" + - "\x14preparation_deadline\x18\f \x01(\v2\x1a.google.protobuf.TimestampR\x13preparationDeadline\x12L\n" + - "\x14admission_start_time\x18\r \x01(\v2\x1a.google.protobuf.TimestampR\x12admissionStartTime\x128\n" + - "\x18driver_operation_pending\x18\x0e \x01(\bR\x16driverOperationPending\x12.\n" + - "\x13driver_operation_id\x18\x0f \x01(\tR\x11driverOperationId\"\xaa\x01\n" + - "\x16SandboxServiceExposure\x12\x18\n" + - "\aservice\x18\x01 \x01(\tR\aservice\x12\x1f\n" + - "\vtarget_port\x18\x02 \x01(\rR\n" + - "targetPort\x12U\n" + - "\x12authorization_mode\x18\x03 \x01(\x0e2&.openshell.v1.ServiceAuthorizationModeR\x11authorizationMode\"\x8a\x03\n" + - "\x0fEndpointFailure\x12;\n" + - "\x06domain\x18\x01 \x01(\x0e2#.openshell.v1.EndpointFailureDomainR\x06domain\x12\x12\n" + - "\x04code\x18\x02 \x01(\tR\x04code\x12^\n" + - "\x0frecovery_action\x18\x03 \x01(\x0e25.openshell.v1.ProviderCredentialRefreshRecoveryActionR\x0erecoveryAction\x12\x1a\n" + - "\bprovider\x18\x04 \x01(\tR\bprovider\x12%\n" + - "\x0ecredential_key\x18\x05 \x01(\tR\rcredentialKey\x12\x14\n" + - "\x05stage\x18\x06 \x01(\tR\x05stage\x125\n" + - "\x14upstream_http_status\x18\a \x01(\rH\x00R\x12upstreamHttpStatus\x88\x01\x01\x12\x1d\n" + - "\n" + - "request_id\x18\b \x01(\tR\trequestIdB\x17\n" + - "\x15_upstream_http_status*\xca\x01\n" + - "\rExtensionKind\x12\x1e\n" + - "\x1aEXTENSION_KIND_UNSPECIFIED\x10\x00\x12!\n" + - "\x1dEXTENSION_KIND_COMPUTE_DRIVER\x10\x01\x12$\n" + - " EXTENSION_KIND_CREDENTIAL_DRIVER\x10\x02\x12&\n" + - "\"EXTENSION_KIND_GATEWAY_INTERCEPTOR\x10\x03\x12(\n" + - "$EXTENSION_KIND_SUPERVISOR_MIDDLEWARE\x10\x04*\x81\x03\n" + - "\fSandboxPhase\x12\x1d\n" + - "\x19SANDBOX_PHASE_UNSPECIFIED\x10\x00\x12\x1e\n" + - "\x1aSANDBOX_PHASE_PROVISIONING\x10\x01\x12\x17\n" + - "\x13SANDBOX_PHASE_READY\x10\x02\x12\x17\n" + - "\x13SANDBOX_PHASE_ERROR\x10\x03\x12\x1a\n" + - "\x16SANDBOX_PHASE_DELETING\x10\x04\x12\x19\n" + - "\x15SANDBOX_PHASE_UNKNOWN\x10\x05\x12\x1a\n" + - "\x16SANDBOX_PHASE_STOPPING\x10\x06\x12\x19\n" + - "\x15SANDBOX_PHASE_STOPPED\x10\a\x12\x1a\n" + - "\x16SANDBOX_PHASE_STARTING\x10\b\x12\x1b\n" + - "\x17SANDBOX_PHASE_COMPLETED\x10\t\x12\x1c\n" + - "\x18SANDBOX_PHASE_SUSPENDING\x10\v\x12\x1b\n" + - "\x17SANDBOX_PHASE_SUSPENDED\x10\f\"\x04\b\n" + - "\x10\n" + - "*\x18SANDBOX_PHASE_RESTARTING*\xcb\x01\n" + - "\x14ProviderMutationKind\x12&\n" + - "\"PROVIDER_MUTATION_KIND_UNSPECIFIED\x10\x00\x12!\n" + - "\x1dPROVIDER_MUTATION_KIND_ATTACH\x10\x01\x12!\n" + - "\x1dPROVIDER_MUTATION_KIND_DETACH\x10\x02\x12!\n" + - "\x1dPROVIDER_MUTATION_KIND_UPDATE\x10\x03\x12\"\n" + - "\x1ePROVIDER_MUTATION_KIND_OBSERVE\x10\x04*\xcf\x02\n" + - "\x16ProviderReadinessState\x12(\n" + - "$PROVIDER_READINESS_STATE_UNSPECIFIED\x10\x00\x12&\n" + - "\"PROVIDER_READINESS_STATE_PERSISTED\x10\x01\x12$\n" + - " PROVIDER_READINESS_STATE_PENDING\x10\x02\x12\"\n" + - "\x1ePROVIDER_READINESS_STATE_READY\x10\x03\x12%\n" + - "!PROVIDER_READINESS_STATE_WITHHELD\x10\x04\x12$\n" + - " PROVIDER_READINESS_STATE_REVOKED\x10\x05\x12#\n" + - "\x1fPROVIDER_READINESS_STATE_FAILED\x10\x06\x12'\n" + - "#PROVIDER_READINESS_STATE_SUPERSEDED\x10\a*\xda\x06\n" + - "\x17ProviderReadinessReason\x12)\n" + - "%PROVIDER_READINESS_REASON_UNSPECIFIED\x10\x00\x124\n" + - "0PROVIDER_READINESS_REASON_WAITING_FOR_SUPERVISOR\x10\x01\x125\n" + - "1PROVIDER_READINESS_REASON_WAITING_FOR_CREDENTIALS\x10\x02\x120\n" + - ",PROVIDER_READINESS_REASON_WAITING_FOR_POLICY\x10\x03\x121\n" + - "-PROVIDER_READINESS_REASON_WAITING_FOR_PROCESS\x10\x04\x124\n" + - "0PROVIDER_READINESS_REASON_UNSUPPORTED_SUPERVISOR\x10\x05\x122\n" + - ".PROVIDER_READINESS_REASON_CREDENTIALS_WITHHELD\x10\x06\x127\n" + - "3PROVIDER_READINESS_REASON_CREDENTIAL_INSTALL_FAILED\x10\a\x126\n" + - "2PROVIDER_READINESS_REASON_POLICY_ACTIVATION_FAILED\x10\b\x124\n" + - "0PROVIDER_READINESS_REASON_PROCESS_INSTALL_FAILED\x10\t\x125\n" + - "1PROVIDER_READINESS_REASON_SUPERVISOR_DISCONNECTED\x10\n" + - "\x126\n" + - "2PROVIDER_READINESS_REASON_SUPERVISOR_LEASE_EXPIRED\x10\v\x123\n" + - "/PROVIDER_READINESS_REASON_DESIRED_STATE_CHANGED\x10\f\x120\n" + - ",PROVIDER_READINESS_REASON_CREDENTIAL_EXPIRED\x10\r\x12*\n" + - "&PROVIDER_READINESS_REASON_LOCAL_POLICY\x10\x0e\x12/\n" + - "+PROVIDER_READINESS_REASON_SNAPSHOT_MISMATCH\x10\x0f*\x83\x01\n" + - "\x0fConfigComponent\x12 \n" + - "\x1cCONFIG_COMPONENT_UNSPECIFIED\x10\x00\x12#\n" + - "\x1fCONFIG_COMPONENT_SANDBOX_CONFIG\x10\x01\x12)\n" + - "%CONFIG_COMPONENT_PROVIDER_ENVIRONMENT\x10\x02*\x8d\x03\n" + - "\x12ConfigApplyOutcome\x12$\n" + - " CONFIG_APPLY_OUTCOME_UNSPECIFIED\x10\x00\x12 \n" + - "\x1cCONFIG_APPLY_OUTCOME_APPLIED\x10\x01\x12*\n" + - "&CONFIG_APPLY_OUTCOME_IGNORED_DUPLICATE\x10\x02\x12&\n" + - "\"CONFIG_APPLY_OUTCOME_IGNORED_STALE\x10\x03\x120\n" + - ",CONFIG_APPLY_OUTCOME_RETAINED_LOCAL_OVERRIDE\x10\x04\x12!\n" + - "\x1dCONFIG_APPLY_OUTCOME_DEGRADED\x10\x05\x128\n" + - "4CONFIG_APPLY_OUTCOME_FAILED_RETAINED_LAST_KNOWN_GOOD\x10\x06\x12&\n" + - "\"CONFIG_APPLY_OUTCOME_FAILED_CLOSED\x10\a\x12$\n" + - " CONFIG_APPLY_OUTCOME_UNSUPPORTED\x10\b*\xd2\x02\n" + - "\x1aConfigUpdateOperationState\x12-\n" + - ")CONFIG_UPDATE_OPERATION_STATE_UNSPECIFIED\x10\x00\x12)\n" + - "%CONFIG_UPDATE_OPERATION_STATE_PENDING\x10\x01\x12)\n" + - "%CONFIG_UPDATE_OPERATION_STATE_APPLIED\x10\x02\x12*\n" + - "&CONFIG_UPDATE_OPERATION_STATE_INACTIVE\x10\x03\x12(\n" + - "$CONFIG_UPDATE_OPERATION_STATE_FAILED\x10\x04\x12,\n" + - "(CONFIG_UPDATE_OPERATION_STATE_SUPERSEDED\x10\x05\x12+\n" + - "'CONFIG_UPDATE_OPERATION_STATE_CANCELLED\x10\x06*\xce\x01\n" + - " ProviderCredentialTokenGrantType\x124\n" + - "0PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_UNSPECIFIED\x10\x00\x12;\n" + - "7PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_CLIENT_CREDENTIALS\x10\x01\x127\n" + - "3PROVIDER_CREDENTIAL_TOKEN_GRANT_TYPE_TOKEN_EXCHANGE\x10\x02*\xc3\x03\n" + - "!ProviderCredentialRefreshStrategy\x124\n" + - "0PROVIDER_CREDENTIAL_REFRESH_STRATEGY_UNSPECIFIED\x10\x00\x12/\n" + - "+PROVIDER_CREDENTIAL_REFRESH_STRATEGY_STATIC\x10\x01\x121\n" + - "-PROVIDER_CREDENTIAL_REFRESH_STRATEGY_EXTERNAL\x10\x02\x12=\n" + - "9PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_REFRESH_TOKEN\x10\x03\x12B\n" + - ">PROVIDER_CREDENTIAL_REFRESH_STRATEGY_OAUTH2_CLIENT_CREDENTIALS\x10\x04\x12C\n" + - "?PROVIDER_CREDENTIAL_REFRESH_STRATEGY_GOOGLE_SERVICE_ACCOUNT_JWT\x10\x05\x12<\n" + - "8PROVIDER_CREDENTIAL_REFRESH_STRATEGY_AWS_STS_ASSUME_ROLE\x10\x06*\xdb\x02\n" + - "\x17ProviderProfileCategory\x12)\n" + - "%PROVIDER_PROFILE_CATEGORY_UNSPECIFIED\x10\x00\x12#\n" + - "\x1fPROVIDER_PROFILE_CATEGORY_OTHER\x10\x01\x12'\n" + - "#PROVIDER_PROFILE_CATEGORY_INFERENCE\x10\x02\x12#\n" + - "\x1fPROVIDER_PROFILE_CATEGORY_AGENT\x10\x03\x12,\n" + - "(PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL\x10\x04\x12'\n" + - "#PROVIDER_PROFILE_CATEGORY_MESSAGING\x10\x05\x12\"\n" + - "\x1ePROVIDER_PROFILE_CATEGORY_DATA\x10\x06\x12'\n" + - "#PROVIDER_PROFILE_CATEGORY_KNOWLEDGE\x10\a*\xde\x01\n" + - "&ProviderEnvironmentValueClassification\x129\n" + - "5PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_UNSPECIFIED\x10\x00\x128\n" + - "4PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_NON_SECRET\x10\x01\x12?\n" + - ";PROVIDER_ENVIRONMENT_VALUE_CLASSIFICATION_STATIC_CREDENTIAL\x10\x02*\xcf\x01\n" + - "\x1bConfigurationAdmissionState\x12-\n" + - ")CONFIGURATION_ADMISSION_STATE_UNSPECIFIED\x10\x00\x12)\n" + - "%CONFIGURATION_ADMISSION_STATE_PENDING\x10\x01\x12*\n" + - "&CONFIGURATION_ADMISSION_STATE_ACCEPTED\x10\x02\x12*\n" + - "&CONFIGURATION_ADMISSION_STATE_REJECTED\x10\x03*\x9a\x01\n" + - "\fPolicyStatus\x12\x1d\n" + - "\x19POLICY_STATUS_UNSPECIFIED\x10\x00\x12\x19\n" + - "\x15POLICY_STATUS_PENDING\x10\x01\x12\x18\n" + - "\x14POLICY_STATUS_LOADED\x10\x02\x12\x18\n" + - "\x14POLICY_STATUS_FAILED\x10\x03\x12\x1c\n" + - "\x18POLICY_STATUS_SUPERSEDED\x10\x04*\x86\x01\n" + - "\rServiceStatus\x12\x1e\n" + - "\x1aSERVICE_STATUS_UNSPECIFIED\x10\x00\x12\x1a\n" + - "\x16SERVICE_STATUS_HEALTHY\x10\x01\x12\x1b\n" + - "\x17SERVICE_STATUS_DEGRADED\x10\x02\x12\x1c\n" + - "\x18SERVICE_STATUS_UNHEALTHY\x10\x03*b\n" + - "\rWorkspaceRole\x12\x1e\n" + - "\x1aWORKSPACE_ROLE_UNSPECIFIED\x10\x00\x12\x17\n" + - "\x13WORKSPACE_ROLE_USER\x10\x01\x12\x18\n" + - "\x14WORKSPACE_ROLE_ADMIN\x10\x02*\xda\x02\n" + - "'ProviderCredentialRefreshRecoveryAction\x12;\n" + - "7PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_UNSPECIFIED\x10\x00\x125\n" + - "1PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_RETRY\x10\x01\x12;\n" + - "7PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_REAUTHORIZE\x10\x02\x12A\n" + - "=PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_FIX_CONFIGURATION\x10\x03\x12;\n" + - "7PROVIDER_CREDENTIAL_REFRESH_RECOVERY_ACTION_INVESTIGATE\x10\x04*\xaa\x01\n" + - "\x14SandboxRestartPolicy\x12&\n" + - "\"SANDBOX_RESTART_POLICY_UNSPECIFIED\x10\x00\x12 \n" + - "\x1cSANDBOX_RESTART_POLICY_NEVER\x10\x01\x12%\n" + - "!SANDBOX_RESTART_POLICY_ON_FAILURE\x10\x02\x12!\n" + - "\x1dSANDBOX_RESTART_POLICY_ALWAYS\x10\x03*\x97\x01\n" + - "\x0fDeletionOutcome\x12 \n" + - "\x1cDELETION_OUTCOME_UNSPECIFIED\x10\x00\x12\x1e\n" + - "\x1aDELETION_OUTCOME_COMPLETED\x10\x01\x12\x1d\n" + - "\x19DELETION_OUTCOME_ACCEPTED\x10\x02\x12#\n" + - "\x1fDELETION_OUTCOME_ALREADY_ABSENT\x10\x03*\xc3\x02\n" + - "\x0eEndpointResult\x12\x1f\n" + - "\x1bENDPOINT_RESULT_UNSPECIFIED\x10\x00\x12(\n" + - "$ENDPOINT_RESULT_NO_OBSERVED_EXCHANGE\x10\x01\x12*\n" + - "&ENDPOINT_RESULT_HTTP_RESPONSE_RECEIVED\x10\x02\x12!\n" + - "\x1dENDPOINT_RESULT_POLICY_DENIED\x10\x03\x12*\n" + - "&ENDPOINT_RESULT_CREDENTIAL_UNAVAILABLE\x10\x04\x12\x1e\n" + - "\x1aENDPOINT_RESULT_TLS_FAILED\x10\x05\x12$\n" + - " ENDPOINT_RESULT_TRANSPORT_FAILED\x10\x06\x12%\n" + - "!ENDPOINT_RESULT_UPSTREAM_REJECTED\x10\a*\x9f\x01\n" + - "\x18ServiceAuthorizationMode\x12*\n" + - "&SERVICE_AUTHORIZATION_MODE_UNSPECIFIED\x10\x00\x12$\n" + - " SERVICE_AUTHORIZATION_MODE_STRIP\x10\x01\x121\n" + - "-SERVICE_AUTHORIZATION_MODE_BEARER_PASSTHROUGH\x10\x02*i\n" + - "\x15EndpointFailureDomain\x12'\n" + - "#ENDPOINT_FAILURE_DOMAIN_UNSPECIFIED\x10\x00\x12'\n" + - "#ENDPOINT_FAILURE_DOMAIN_TOKEN_GRANT\x10\x012\xa6Y\n" + - "\tOpenShell\x12Z\n" + - "\x06Health\x12\x1b.openshell.v1.HealthRequest\x1a\x1c.openshell.v1.HealthResponse\"\x15\x82\xb5\x18\x11\n" + - "\x0funauthenticated\x12i\n" + - "\x0eGetCurrentUser\x12#.openshell.v1.GetCurrentUserRequest\x1a$.openshell.v1.GetCurrentUserResponse\"\f\x82\xb5\x18\b\n" + - "\x06bearer\x12\x86\x01\n" + - "\x0eGetGatewayInfo\x12#.openshell.v1.GetGatewayInfoRequest\x1a$.openshell.v1.GetGatewayInfoResponse\")\x82\xb5\x18%\n" + - "\x06bearer\x1a\x0eplatform_admin\"\vconfig:read\x12u\n" + - "\rCreateSandbox\x12\".openshell.v1.CreateSandboxRequest\x1a\x1d.openshell.v1.SandboxResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12\x93\x01\n" + - "\x15BeginRootfsTarStaging\x12*.openshell.v1.BeginRootfsTarStagingRequest\x1a+.openshell.v1.BeginRootfsTarStagingResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12n\n" + - "\n" + - "GetSandbox\x12\x1f.openshell.v1.GetSandboxRequest\x1a\x1d.openshell.v1.SandboxResponse\" \x82\xb5\x18\x1c\n" + - "\x06bearer\x12\x04user\"\fsandbox:read\x12z\n" + - "\rListSandboxes\x12\".openshell.v1.ListSandboxesRequest\x1a#.openshell.v1.ListSandboxesResponse\" \x82\xb5\x18\x1c\n" + - "\x06bearer\x12\x04user\"\fsandbox:read\x12\x8e\x01\n" + - "\x15CreateSandboxTemplate\x12*.openshell.v1.CreateSandboxTemplateRequest\x1a%.openshell.v1.SandboxTemplateResponse\"\"\x82\xb5\x18\x1e\n" + - "\x06bearer\x12\x05admin\"\rsandbox:write\x12\x86\x01\n" + - "\x12GetSandboxTemplate\x12'.openshell.v1.GetSandboxTemplateRequest\x1a%.openshell.v1.SandboxTemplateResponse\" \x82\xb5\x18\x1c\n" + - "\x06bearer\x12\x04user\"\fsandbox:read\x12\x8f\x01\n" + - "\x14ListSandboxTemplates\x12).openshell.v1.ListSandboxTemplatesRequest\x1a*.openshell.v1.ListSandboxTemplatesResponse\" \x82\xb5\x18\x1c\n" + - "\x06bearer\x12\x04user\"\fsandbox:read\x12\x94\x01\n" + - "\x15DeleteSandboxTemplate\x12*.openshell.v1.DeleteSandboxTemplateRequest\x1a+.openshell.v1.DeleteSandboxTemplateResponse\"\"\x82\xb5\x18\x1e\n" + - "\x06bearer\x12\x05admin\"\rsandbox:write\x12\x8f\x01\n" + - "\x14ListSandboxProviders\x12).openshell.v1.ListSandboxProvidersRequest\x1a*.openshell.v1.ListSandboxProvidersResponse\" \x82\xb5\x18\x1c\n" + - "\x06bearer\x12\x04user\"\fsandbox:read\x12\x93\x01\n" + - "\x15AttachSandboxProvider\x12*.openshell.v1.AttachSandboxProviderRequest\x1a+.openshell.v1.AttachSandboxProviderResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12\x93\x01\n" + - "\x15DetachSandboxProvider\x12*.openshell.v1.DetachSandboxProviderRequest\x1a+.openshell.v1.DetachSandboxProviderResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12\x9b\x01\n" + - "\x18GetSandboxProviderStatus\x12-.openshell.v1.GetSandboxProviderStatusRequest\x1a..openshell.v1.GetSandboxProviderStatusResponse\" \x82\xb5\x18\x1c\n" + - "\x06bearer\x12\x04user\"\fsandbox:read\x12{\n" + - "\rDeleteSandbox\x12\".openshell.v1.DeleteSandboxRequest\x1a#.openshell.v1.DeleteSandboxResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12q\n" + - "\vStopSandbox\x12 .openshell.v1.StopSandboxRequest\x1a\x1d.openshell.v1.SandboxResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12s\n" + - "\fStartSandbox\x12!.openshell.v1.StartSandboxRequest\x1a\x1d.openshell.v1.SandboxResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12w\n" + - "\x0eSuspendSandbox\x12#.openshell.v1.SuspendSandboxRequest\x1a\x1d.openshell.v1.SandboxResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12u\n" + - "\rResumeSandbox\x12\".openshell.v1.ResumeSandboxRequest\x1a\x1d.openshell.v1.SandboxResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12\x84\x01\n" + - "\x10CreateSshSession\x12%.openshell.v1.CreateSshSessionRequest\x1a&.openshell.v1.CreateSshSessionResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12}\n" + - "\rExposeService\x12\".openshell.v1.ExposeServiceRequest\x1a%.openshell.v1.ServiceEndpointResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12v\n" + - "\n" + - "GetService\x12\x1f.openshell.v1.GetServiceRequest\x1a%.openshell.v1.ServiceEndpointResponse\" \x82\xb5\x18\x1c\n" + - "\x06bearer\x12\x04user\"\fsandbox:read\x12w\n" + - "\fListServices\x12!.openshell.v1.ListServicesRequest\x1a\".openshell.v1.ListServicesResponse\" \x82\xb5\x18\x1c\n" + - "\x06bearer\x12\x04user\"\fsandbox:read\x12{\n" + - "\rDeleteService\x12\".openshell.v1.DeleteServiceRequest\x1a#.openshell.v1.DeleteServiceResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12\x84\x01\n" + - "\x10RevokeSshSession\x12%.openshell.v1.RevokeSshSessionRequest\x1a&.openshell.v1.RevokeSshSessionResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write\x12t\n" + - "\vExecSandbox\x12 .openshell.v1.ExecSandboxRequest\x1a\x1e.openshell.v1.ExecSandboxEvent\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write0\x01\x12q\n" + - "\n" + - "ForwardTcp\x12\x1d.openshell.v1.TcpForwardFrame\x1a\x1d.openshell.v1.TcpForwardFrame\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write(\x010\x01\x12\x7f\n" + - "\x16ExecSandboxInteractive\x12\x1e.openshell.v1.ExecSandboxInput\x1a\x1e.openshell.v1.ExecSandboxEvent\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:write(\x010\x01\x12z\n" + - "\x0eCreateProvider\x12#.openshell.v1.CreateProviderRequest\x1a\x1e.openshell.v1.ProviderResponse\"#\x82\xb5\x18\x1f\n" + - "\x06bearer\x12\x05admin\"\x0eprovider:write\x12r\n" + - "\vGetProvider\x12 .openshell.v1.GetProviderRequest\x1a\x1e.openshell.v1.ProviderResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rprovider:read\x12\x83\x01\n" + - "\x16GetProviderCredentials\x12+.openshell.v1.GetProviderCredentialsRequest\x1a,.openshell.v1.GetProviderCredentialsResponse\"\x0e\x82\xb5\x18\n" + - "\n" + - "\boperator\x12{\n" + - "\rListProviders\x12\".openshell.v1.ListProvidersRequest\x1a#.openshell.v1.ListProvidersResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rprovider:read\x12\x90\x01\n" + - "\x14ListProviderProfiles\x12).openshell.v1.ListProviderProfilesRequest\x1a*.openshell.v1.ListProviderProfilesResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rprovider:read\x12\x87\x01\n" + - "\x12GetProviderProfile\x12'.openshell.v1.GetProviderProfileRequest\x1a%.openshell.v1.ProviderProfileResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rprovider:read\x12\x98\x01\n" + - "\x16ImportProviderProfiles\x12+.openshell.v1.ImportProviderProfilesRequest\x1a,.openshell.v1.ImportProviderProfilesResponse\"#\x82\xb5\x18\x1f\n" + - "\x06bearer\x12\x05admin\"\x0eprovider:write\x12\x98\x01\n" + - "\x16UpdateProviderProfiles\x12+.openshell.v1.UpdateProviderProfilesRequest\x1a,.openshell.v1.UpdateProviderProfilesResponse\"#\x82\xb5\x18\x1f\n" + - "\x06bearer\x12\x05admin\"\x0eprovider:write\x12\x90\x01\n" + - "\x14LintProviderProfiles\x12).openshell.v1.LintProviderProfilesRequest\x1a*.openshell.v1.LintProviderProfilesResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rprovider:read\x12z\n" + - "\x0eUpdateProvider\x12#.openshell.v1.UpdateProviderRequest\x1a\x1e.openshell.v1.ProviderResponse\"#\x82\xb5\x18\x1f\n" + - "\x06bearer\x12\x05admin\"\x0eprovider:write\x12\x9c\x01\n" + - "\x18GetProviderRefreshStatus\x12-.openshell.v1.GetProviderRefreshStatusRequest\x1a..openshell.v1.GetProviderRefreshStatusResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rprovider:read\x12\x9e\x01\n" + - "\x18ConfigureProviderRefresh\x12-.openshell.v1.ConfigureProviderRefreshRequest\x1a..openshell.v1.ConfigureProviderRefreshResponse\"#\x82\xb5\x18\x1f\n" + - "\x06bearer\x12\x05admin\"\x0eprovider:write\x12\x9e\x01\n" + - "\x18RotateProviderCredential\x12-.openshell.v1.RotateProviderCredentialRequest\x1a..openshell.v1.RotateProviderCredentialResponse\"#\x82\xb5\x18\x1f\n" + - "\x06bearer\x12\x05admin\"\x0eprovider:write\x12\x95\x01\n" + - "\x15DeleteProviderRefresh\x12*.openshell.v1.DeleteProviderRefreshRequest\x1a+.openshell.v1.DeleteProviderRefreshResponse\"#\x82\xb5\x18\x1f\n" + - "\x06bearer\x12\x05admin\"\x0eprovider:write\x12\x80\x01\n" + - "\x0eDeleteProvider\x12#.openshell.v1.DeleteProviderRequest\x1a$.openshell.v1.DeleteProviderResponse\"#\x82\xb5\x18\x1f\n" + - "\x06bearer\x12\x05admin\"\x0eprovider:write\x12\x95\x01\n" + - "\x15DeleteProviderProfile\x12*.openshell.v1.DeleteProviderProfileRequest\x1a+.openshell.v1.DeleteProviderProfileResponse\"#\x82\xb5\x18\x1f\n" + - "\x06bearer\x12\x05admin\"\x0eprovider:write\x12\x90\x01\n" + - "\x10GetSandboxConfig\x12-.openshell.sandbox.v1.GetSandboxConfigRequest\x1a..openshell.sandbox.v1.GetSandboxConfigResponse\"\x1d\x82\xb5\x18\x19\n" + - "\x04dual\x12\x04user\"\vconfig:read\x12\x8c\x01\n" + - "\x10GetGatewayConfig\x12-.openshell.sandbox.v1.GetGatewayConfigRequest\x1a..openshell.sandbox.v1.GetGatewayConfigResponse\"\x19\x82\xb5\x18\x15\n" + - "\x06bearer\"\vconfig:read\x12v\n" + - "\fUpdateConfig\x12!.openshell.v1.UpdateConfigRequest\x1a\".openshell.v1.UpdateConfigResponse\"\x1f\x82\xb5\x18\x1b\n" + - "\x04dual\x12\x05admin\"\fconfig:write\x12\x95\x01\n" + - "\x16GetSandboxPolicyStatus\x12+.openshell.v1.GetSandboxPolicyStatusRequest\x1a,.openshell.v1.GetSandboxPolicyStatusResponse\" \x82\xb5\x18\x1c\n" + - "\x06bearer\x12\x04user\"\fsandbox:read\x12\x8c\x01\n" + - "\x13ListSandboxPolicies\x12(.openshell.v1.ListSandboxPoliciesRequest\x1a).openshell.v1.ListSandboxPoliciesResponse\" \x82\xb5\x18\x1c\n" + - "\x06bearer\x12\x04user\"\fsandbox:read\x12v\n" + - "\x12ReportPolicyStatus\x12'.openshell.v1.ReportPolicyStatusRequest\x1a(.openshell.v1.ReportPolicyStatusResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12|\n" + - "\x14ReportEndpointStatus\x12).openshell.v1.ReportEndpointStatusRequest\x1a*.openshell.v1.ReportEndpointStatusResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12\x85\x01\n" + - "\x17ReportProviderReadiness\x12,.openshell.v1.ReportProviderReadinessRequest\x1a-.openshell.v1.ReportProviderReadinessResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12\x8e\x01\n" + - "\x1aReportSandboxConfiguration\x12/.openshell.v1.ReportSandboxConfigurationRequest\x1a0.openshell.v1.ReportSandboxConfigurationResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12\x97\x01\n" + - "\x1dGetSandboxProviderEnvironment\x122.openshell.v1.GetSandboxProviderEnvironmentRequest\x1a3.openshell.v1.GetSandboxProviderEnvironmentResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12\x94\x01\n" + - "\x1cExchangeProviderSubjectToken\x121.openshell.v1.ExchangeProviderSubjectTokenRequest\x1a2.openshell.v1.ExchangeProviderSubjectTokenResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12}\n" + - "\x0eGetSandboxLogs\x12#.openshell.v1.GetSandboxLogsRequest\x1a$.openshell.v1.GetSandboxLogsResponse\" \x82\xb5\x18\x1c\n" + - "\x06bearer\x12\x04user\"\fsandbox:read\x12o\n" + - "\x0fPushSandboxLogs\x12$.openshell.v1.PushSandboxLogsRequest\x1a%.openshell.v1.PushSandboxLogsResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox(\x01\x12e\n" + - "\x11ConnectSupervisor\x12\x1f.openshell.v1.SupervisorMessage\x1a\x1c.openshell.v1.GatewayMessage\"\r\x82\xb5\x18\t\n" + - "\asandbox(\x010\x01\x12\x7f\n" + - "\x15ReportMainProcessExit\x12*.openshell.v1.ReportMainProcessExitRequest\x1a+.openshell.v1.ReportMainProcessExitResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12\x85\x01\n" + - "\x17FinalizeMainProcessExit\x12,.openshell.v1.FinalizeMainProcessExitRequest\x1a-.openshell.v1.FinalizeMainProcessExitResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12T\n" + - "\vRelayStream\x12\x18.openshell.v1.RelayFrame\x1a\x18.openshell.v1.RelayFrame\"\r\x82\xb5\x18\t\n" + - "\asandbox(\x010\x01\x12W\n" + - "\tPeerRelay\x12\x1c.openshell.v1.PeerRelayFrame\x1a\x1c.openshell.v1.PeerRelayFrame\"\n" + - "\x82\xb5\x18\x06\n" + - "\x04peer(\x010\x01\x12\x86\x01\n" + - "\x1bPeerReportProviderReadiness\x12,.openshell.v1.ReportProviderReadinessRequest\x1a-.openshell.v1.ReportProviderReadinessResponse\"\n" + - "\x82\xb5\x18\x06\n" + - "\x04peer\x12}\n" + - "\x18PeerReportEndpointStatus\x12).openshell.v1.ReportEndpointStatusRequest\x1a*.openshell.v1.ReportEndpointStatusResponse\"\n" + - "\x82\xb5\x18\x06\n" + - "\x04peer\x12\x89\x01\n" + - "\x1cPeerGetSandboxProviderStatus\x12-.openshell.v1.GetSandboxProviderStatusRequest\x1a..openshell.v1.GetSandboxProviderStatusResponse\"\n" + - "\x82\xb5\x18\x06\n" + - "\x04peer\x12\x7f\n" + - "\x16PeerNotifyConfigUpdate\x12+.openshell.v1.PeerNotifyConfigUpdateRequest\x1a,.openshell.v1.PeerNotifyConfigUpdateResponse\"\n" + - "\x82\xb5\x18\x06\n" + - "\x04peer\x12w\n" + - "\fWatchSandbox\x12!.openshell.v1.WatchSandboxRequest\x1a .openshell.v1.SandboxStreamEvent\" \x82\xb5\x18\x1c\n" + - "\x06bearer\x12\x04user\"\fsandbox:read0\x01\x12|\n" + - "\x14SubmitPolicyAnalysis\x12).openshell.v1.SubmitPolicyAnalysisRequest\x1a*.openshell.v1.SubmitPolicyAnalysisResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12z\n" + - "\x0eGetDraftPolicy\x12#.openshell.v1.GetDraftPolicyRequest\x1a$.openshell.v1.GetDraftPolicyResponse\"\x1d\x82\xb5\x18\x19\n" + - "\x04dual\x12\x04user\"\vconfig:read\x12\x87\x01\n" + - "\x11ApproveDraftChunk\x12&.openshell.v1.ApproveDraftChunkRequest\x1a'.openshell.v1.ApproveDraftChunkResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x05admin\"\fconfig:write\x12\x84\x01\n" + - "\x10RejectDraftChunk\x12%.openshell.v1.RejectDraftChunkRequest\x1a&.openshell.v1.RejectDraftChunkResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x05admin\"\fconfig:write\x12\x93\x01\n" + - "\x15ApproveAllDraftChunks\x12*.openshell.v1.ApproveAllDraftChunksRequest\x1a+.openshell.v1.ApproveAllDraftChunksResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x05admin\"\fconfig:write\x12~\n" + - "\x0eEditDraftChunk\x12#.openshell.v1.EditDraftChunkRequest\x1a$.openshell.v1.EditDraftChunkResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x05admin\"\fconfig:write\x12~\n" + - "\x0eUndoDraftChunk\x12#.openshell.v1.UndoDraftChunkRequest\x1a$.openshell.v1.UndoDraftChunkResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x05admin\"\fconfig:write\x12\x84\x01\n" + - "\x10ClearDraftChunks\x12%.openshell.v1.ClearDraftChunksRequest\x1a&.openshell.v1.ClearDraftChunksResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x05admin\"\fconfig:write\x12\x7f\n" + - "\x0fGetDraftHistory\x12$.openshell.v1.GetDraftHistoryRequest\x1a%.openshell.v1.GetDraftHistoryResponse\"\x1f\x82\xb5\x18\x1b\n" + - "\x06bearer\x12\x04user\"\vconfig:read\x12s\n" + - "\x11IssueSandboxToken\x12&.openshell.v1.IssueSandboxTokenRequest\x1a'.openshell.v1.IssueSandboxTokenResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12y\n" + - "\x13RefreshSandboxToken\x12(.openshell.v1.RefreshSandboxTokenRequest\x1a).openshell.v1.RefreshSandboxTokenResponse\"\r\x82\xb5\x18\t\n" + - "\asandbox\x12\x8d\x01\n" + - "\x0fCreateWorkspace\x12$.openshell.v1.CreateWorkspaceRequest\x1a%.openshell.v1.CreateWorkspaceResponse\"-\x82\xb5\x18)\n" + - "\x06bearer\x1a\x0eplatform_admin\"\x0fworkspace:write\x12y\n" + - "\fGetWorkspace\x12!.openshell.v1.GetWorkspaceRequest\x1a\".openshell.v1.GetWorkspaceResponse\"\"\x82\xb5\x18\x1e\n" + - "\x06bearer\x12\x04user\"\x0eworkspace:read\x12\x7f\n" + - "\x0eListWorkspaces\x12#.openshell.v1.ListWorkspacesRequest\x1a$.openshell.v1.ListWorkspacesResponse\"\"\x82\xb5\x18\x1e\n" + - "\x06bearer\x12\x04user\"\x0eworkspace:read\x12\x8d\x01\n" + - "\x0fDeleteWorkspace\x12$.openshell.v1.DeleteWorkspaceRequest\x1a%.openshell.v1.DeleteWorkspaceResponse\"-\x82\xb5\x18)\n" + - "\x06bearer\x1a\x0eplatform_admin\"\x0fworkspace:write\x12\x8d\x01\n" + - "\x12AddWorkspaceMember\x12'.openshell.v1.AddWorkspaceMemberRequest\x1a(.openshell.v1.AddWorkspaceMemberResponse\"$\x82\xb5\x18 \n" + - "\x06bearer\x12\x05admin\"\x0fworkspace:write\x12\x96\x01\n" + - "\x15RemoveWorkspaceMember\x12*.openshell.v1.RemoveWorkspaceMemberRequest\x1a+.openshell.v1.RemoveWorkspaceMemberResponse\"$\x82\xb5\x18 \n" + - "\x06bearer\x12\x05admin\"\x0fworkspace:write\x12\x91\x01\n" + - "\x14ListWorkspaceMembers\x12).openshell.v1.ListWorkspaceMembersRequest\x1a*.openshell.v1.ListWorkspaceMembersResponse\"\"\x82\xb5\x18\x1e\n" + - "\x06bearer\x12\x04user\"\x0eworkspace:read2\x87\x01\n" + - "\x0eSandboxUpdates\x12u\n" + - "\rUpdateSandbox\x12\".openshell.v1.UpdateSandboxRequest\x1a\x1d.openshell.v1.SandboxResponse\"!\x82\xb5\x18\x1d\n" + - "\x06bearer\x12\x04user\"\rsandbox:writeb\x06proto3" - -var ( - file_openshell_proto_rawDescOnce sync.Once - file_openshell_proto_rawDescData []byte -) - -func file_openshell_proto_rawDescGZIP() []byte { - file_openshell_proto_rawDescOnce.Do(func() { - file_openshell_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_openshell_proto_rawDesc), len(file_openshell_proto_rawDesc))) - }) - return file_openshell_proto_rawDescData -} - -var file_openshell_proto_enumTypes = make([]protoimpl.EnumInfo, 22) -var file_openshell_proto_msgTypes = make([]protoimpl.MessageInfo, 281) -var file_openshell_proto_goTypes = []any{ - (ExtensionKind)(0), // 0: openshell.v1.ExtensionKind - (SandboxPhase)(0), // 1: openshell.v1.SandboxPhase - (ProviderMutationKind)(0), // 2: openshell.v1.ProviderMutationKind - (ProviderReadinessState)(0), // 3: openshell.v1.ProviderReadinessState - (ProviderReadinessReason)(0), // 4: openshell.v1.ProviderReadinessReason - (ConfigComponent)(0), // 5: openshell.v1.ConfigComponent - (ConfigApplyOutcome)(0), // 6: openshell.v1.ConfigApplyOutcome - (ConfigUpdateOperationState)(0), // 7: openshell.v1.ConfigUpdateOperationState - (ProviderCredentialTokenGrantType)(0), // 8: openshell.v1.ProviderCredentialTokenGrantType - (ProviderCredentialRefreshStrategy)(0), // 9: openshell.v1.ProviderCredentialRefreshStrategy - (ProviderProfileCategory)(0), // 10: openshell.v1.ProviderProfileCategory - (ProviderEnvironmentValueClassification)(0), // 11: openshell.v1.ProviderEnvironmentValueClassification - (ConfigurationAdmissionState)(0), // 12: openshell.v1.ConfigurationAdmissionState - (PolicyStatus)(0), // 13: openshell.v1.PolicyStatus - (ServiceStatus)(0), // 14: openshell.v1.ServiceStatus - (WorkspaceRole)(0), // 15: openshell.v1.WorkspaceRole - (ProviderCredentialRefreshRecoveryAction)(0), // 16: openshell.v1.ProviderCredentialRefreshRecoveryAction - (SandboxRestartPolicy)(0), // 17: openshell.v1.SandboxRestartPolicy - (DeletionOutcome)(0), // 18: openshell.v1.DeletionOutcome - (EndpointResult)(0), // 19: openshell.v1.EndpointResult - (ServiceAuthorizationMode)(0), // 20: openshell.v1.ServiceAuthorizationMode - (EndpointFailureDomain)(0), // 21: openshell.v1.EndpointFailureDomain - (*IssueSandboxTokenRequest)(nil), // 22: openshell.v1.IssueSandboxTokenRequest - (*IssueSandboxTokenResponse)(nil), // 23: openshell.v1.IssueSandboxTokenResponse - (*RefreshSandboxTokenRequest)(nil), // 24: openshell.v1.RefreshSandboxTokenRequest - (*RefreshSandboxTokenResponse)(nil), // 25: openshell.v1.RefreshSandboxTokenResponse - (*HealthRequest)(nil), // 26: openshell.v1.HealthRequest - (*HealthResponse)(nil), // 27: openshell.v1.HealthResponse - (*GetCurrentUserRequest)(nil), // 28: openshell.v1.GetCurrentUserRequest - (*GetCurrentUserResponse)(nil), // 29: openshell.v1.GetCurrentUserResponse - (*GetGatewayInfoRequest)(nil), // 30: openshell.v1.GetGatewayInfoRequest - (*GetGatewayInfoResponse)(nil), // 31: openshell.v1.GetGatewayInfoResponse - (*NegotiatedExtensionInfo)(nil), // 32: openshell.v1.NegotiatedExtensionInfo - (*ComputeDriverInfo)(nil), // 33: openshell.v1.ComputeDriverInfo - (*ComputeDriverCapabilities)(nil), // 34: openshell.v1.ComputeDriverCapabilities - (*ResourceCapabilities)(nil), // 35: openshell.v1.ResourceCapabilities - (*CpuResourceCapabilities)(nil), // 36: openshell.v1.CpuResourceCapabilities - (*MemoryResourceCapabilities)(nil), // 37: openshell.v1.MemoryResourceCapabilities - (*GpuResourceCapabilities)(nil), // 38: openshell.v1.GpuResourceCapabilities - (*Sandbox)(nil), // 39: openshell.v1.Sandbox - (*SandboxSpec)(nil), // 40: openshell.v1.SandboxSpec - (*ResourceRequirements)(nil), // 41: openshell.v1.ResourceRequirements - (*GpuResourceRequirements)(nil), // 42: openshell.v1.GpuResourceRequirements - (*SandboxTemplate)(nil), // 43: openshell.v1.SandboxTemplate - (*SandboxWorkloadTemplate)(nil), // 44: openshell.v1.SandboxWorkloadTemplate - (*SandboxWorkloadTemplateSpec)(nil), // 45: openshell.v1.SandboxWorkloadTemplateSpec - (*SandboxWorkloadConfig)(nil), // 46: openshell.v1.SandboxWorkloadConfig - (*SandboxResources)(nil), // 47: openshell.v1.SandboxResources - (*SandboxServiceLevel)(nil), // 48: openshell.v1.SandboxServiceLevel - (*SandboxStartup)(nil), // 49: openshell.v1.SandboxStartup - (*SandboxWorkloadTemplateProvenance)(nil), // 50: openshell.v1.SandboxWorkloadTemplateProvenance - (*SandboxStatus)(nil), // 51: openshell.v1.SandboxStatus - (*SandboxCondition)(nil), // 52: openshell.v1.SandboxCondition - (*PlatformEvent)(nil), // 53: openshell.v1.PlatformEvent - (*CreateSandboxRequest)(nil), // 54: openshell.v1.CreateSandboxRequest - (*UpdateSandboxRequest)(nil), // 55: openshell.v1.UpdateSandboxRequest - (*SandboxCommandUpdate)(nil), // 56: openshell.v1.SandboxCommandUpdate - (*SandboxMapUpdate)(nil), // 57: openshell.v1.SandboxMapUpdate - (*CreateSandboxTemplateRequest)(nil), // 58: openshell.v1.CreateSandboxTemplateRequest - (*GetSandboxTemplateRequest)(nil), // 59: openshell.v1.GetSandboxTemplateRequest - (*ListSandboxTemplatesRequest)(nil), // 60: openshell.v1.ListSandboxTemplatesRequest - (*DeleteSandboxTemplateRequest)(nil), // 61: openshell.v1.DeleteSandboxTemplateRequest - (*SandboxTemplateResponse)(nil), // 62: openshell.v1.SandboxTemplateResponse - (*ListSandboxTemplatesResponse)(nil), // 63: openshell.v1.ListSandboxTemplatesResponse - (*DeleteSandboxTemplateResponse)(nil), // 64: openshell.v1.DeleteSandboxTemplateResponse - (*BeginRootfsTarStagingRequest)(nil), // 65: openshell.v1.BeginRootfsTarStagingRequest - (*BeginRootfsTarStagingResponse)(nil), // 66: openshell.v1.BeginRootfsTarStagingResponse - (*GetSandboxRequest)(nil), // 67: openshell.v1.GetSandboxRequest - (*ListSandboxesRequest)(nil), // 68: openshell.v1.ListSandboxesRequest - (*ListSandboxProvidersRequest)(nil), // 69: openshell.v1.ListSandboxProvidersRequest - (*AttachSandboxProviderRequest)(nil), // 70: openshell.v1.AttachSandboxProviderRequest - (*DetachSandboxProviderRequest)(nil), // 71: openshell.v1.DetachSandboxProviderRequest - (*DeleteSandboxRequest)(nil), // 72: openshell.v1.DeleteSandboxRequest - (*StopSandboxRequest)(nil), // 73: openshell.v1.StopSandboxRequest - (*StartSandboxRequest)(nil), // 74: openshell.v1.StartSandboxRequest - (*SuspendSandboxRequest)(nil), // 75: openshell.v1.SuspendSandboxRequest - (*ResumeSandboxRequest)(nil), // 76: openshell.v1.ResumeSandboxRequest - (*SandboxResponse)(nil), // 77: openshell.v1.SandboxResponse - (*ListSandboxesResponse)(nil), // 78: openshell.v1.ListSandboxesResponse - (*ListSandboxProvidersResponse)(nil), // 79: openshell.v1.ListSandboxProvidersResponse - (*AttachSandboxProviderResponse)(nil), // 80: openshell.v1.AttachSandboxProviderResponse - (*DetachSandboxProviderResponse)(nil), // 81: openshell.v1.DetachSandboxProviderResponse - (*ProviderDesiredIdentity)(nil), // 82: openshell.v1.ProviderDesiredIdentity - (*ConfigSnapshotRevision)(nil), // 83: openshell.v1.ConfigSnapshotRevision - (*SandboxConfigRevision)(nil), // 84: openshell.v1.SandboxConfigRevision - (*ConfigUpdateOperation)(nil), // 85: openshell.v1.ConfigUpdateOperation - (*ProviderMutationReceipt)(nil), // 86: openshell.v1.ProviderMutationReceipt - (*ProviderReadinessObservation)(nil), // 87: openshell.v1.ProviderReadinessObservation - (*ProviderReadinessStatus)(nil), // 88: openshell.v1.ProviderReadinessStatus - (*GetSandboxProviderStatusRequest)(nil), // 89: openshell.v1.GetSandboxProviderStatusRequest - (*GetSandboxProviderStatusResponse)(nil), // 90: openshell.v1.GetSandboxProviderStatusResponse - (*ReportProviderReadinessRequest)(nil), // 91: openshell.v1.ReportProviderReadinessRequest - (*ReportProviderReadinessResponse)(nil), // 92: openshell.v1.ReportProviderReadinessResponse - (*DeleteSandboxResponse)(nil), // 93: openshell.v1.DeleteSandboxResponse - (*CreateSshSessionRequest)(nil), // 94: openshell.v1.CreateSshSessionRequest - (*CreateSshSessionResponse)(nil), // 95: openshell.v1.CreateSshSessionResponse - (*ExposeServiceRequest)(nil), // 96: openshell.v1.ExposeServiceRequest - (*GetServiceRequest)(nil), // 97: openshell.v1.GetServiceRequest - (*ListServicesRequest)(nil), // 98: openshell.v1.ListServicesRequest - (*ListServicesResponse)(nil), // 99: openshell.v1.ListServicesResponse - (*DeleteServiceRequest)(nil), // 100: openshell.v1.DeleteServiceRequest - (*DeleteServiceResponse)(nil), // 101: openshell.v1.DeleteServiceResponse - (*ServiceEndpoint)(nil), // 102: openshell.v1.ServiceEndpoint - (*ServiceEndpointResponse)(nil), // 103: openshell.v1.ServiceEndpointResponse - (*RevokeSshSessionRequest)(nil), // 104: openshell.v1.RevokeSshSessionRequest - (*RevokeSshSessionResponse)(nil), // 105: openshell.v1.RevokeSshSessionResponse - (*ExecSandboxRequest)(nil), // 106: openshell.v1.ExecSandboxRequest - (*ExecSandboxStdout)(nil), // 107: openshell.v1.ExecSandboxStdout - (*ExecSandboxStderr)(nil), // 108: openshell.v1.ExecSandboxStderr - (*ExecSandboxExit)(nil), // 109: openshell.v1.ExecSandboxExit - (*ExecSandboxEvent)(nil), // 110: openshell.v1.ExecSandboxEvent - (*TcpForwardInit)(nil), // 111: openshell.v1.TcpForwardInit - (*TcpForwardFrame)(nil), // 112: openshell.v1.TcpForwardFrame - (*ExecSandboxInput)(nil), // 113: openshell.v1.ExecSandboxInput - (*ExecSandboxWindowResize)(nil), // 114: openshell.v1.ExecSandboxWindowResize - (*SshSession)(nil), // 115: openshell.v1.SshSession - (*WatchSandboxRequest)(nil), // 116: openshell.v1.WatchSandboxRequest - (*SandboxStreamEvent)(nil), // 117: openshell.v1.SandboxStreamEvent - (*SandboxLogLine)(nil), // 118: openshell.v1.SandboxLogLine - (*SandboxStreamWarning)(nil), // 119: openshell.v1.SandboxStreamWarning - (*CreateProviderRequest)(nil), // 120: openshell.v1.CreateProviderRequest - (*GetProviderRequest)(nil), // 121: openshell.v1.GetProviderRequest - (*GetProviderCredentialsRequest)(nil), // 122: openshell.v1.GetProviderCredentialsRequest - (*ProviderCredentialValue)(nil), // 123: openshell.v1.ProviderCredentialValue - (*GetProviderCredentialsResponse)(nil), // 124: openshell.v1.GetProviderCredentialsResponse - (*ListProvidersRequest)(nil), // 125: openshell.v1.ListProvidersRequest - (*UpdateProviderRequest)(nil), // 126: openshell.v1.UpdateProviderRequest - (*DeleteProviderRequest)(nil), // 127: openshell.v1.DeleteProviderRequest - (*ProviderResponse)(nil), // 128: openshell.v1.ProviderResponse - (*ListProvidersResponse)(nil), // 129: openshell.v1.ListProvidersResponse - (*ListProviderProfilesRequest)(nil), // 130: openshell.v1.ListProviderProfilesRequest - (*GetProviderProfileRequest)(nil), // 131: openshell.v1.GetProviderProfileRequest - (*ProviderProfileImportItem)(nil), // 132: openshell.v1.ProviderProfileImportItem - (*ProviderProfileDiagnostic)(nil), // 133: openshell.v1.ProviderProfileDiagnostic - (*ProviderCredentialTokenGrantAudienceOverride)(nil), // 134: openshell.v1.ProviderCredentialTokenGrantAudienceOverride - (*ProviderCredentialTokenGrantSubjectToken)(nil), // 135: openshell.v1.ProviderCredentialTokenGrantSubjectToken - (*ProviderCredentialTokenGrant)(nil), // 136: openshell.v1.ProviderCredentialTokenGrant - (*ProviderProfileCredential)(nil), // 137: openshell.v1.ProviderProfileCredential - (*ProviderCredentialRefreshMaterial)(nil), // 138: openshell.v1.ProviderCredentialRefreshMaterial - (*ProviderCredentialRefreshOutput)(nil), // 139: openshell.v1.ProviderCredentialRefreshOutput - (*ProviderCredentialRefresh)(nil), // 140: openshell.v1.ProviderCredentialRefresh - (*ProviderCredentialRefreshStatus)(nil), // 141: openshell.v1.ProviderCredentialRefreshStatus - (*ProviderProfileDiscovery)(nil), // 142: openshell.v1.ProviderProfileDiscovery - (*GetProviderRefreshStatusRequest)(nil), // 143: openshell.v1.GetProviderRefreshStatusRequest - (*GetProviderRefreshStatusResponse)(nil), // 144: openshell.v1.GetProviderRefreshStatusResponse - (*ConfigureProviderRefreshRequest)(nil), // 145: openshell.v1.ConfigureProviderRefreshRequest - (*ConfigureProviderRefreshResponse)(nil), // 146: openshell.v1.ConfigureProviderRefreshResponse - (*RotateProviderCredentialRequest)(nil), // 147: openshell.v1.RotateProviderCredentialRequest - (*RotateProviderCredentialResponse)(nil), // 148: openshell.v1.RotateProviderCredentialResponse - (*DeleteProviderRefreshRequest)(nil), // 149: openshell.v1.DeleteProviderRefreshRequest - (*DeleteProviderRefreshResponse)(nil), // 150: openshell.v1.DeleteProviderRefreshResponse - (*ProviderProfile)(nil), // 151: openshell.v1.ProviderProfile - (*ProviderProfileFile)(nil), // 152: openshell.v1.ProviderProfileFile - (*ProviderProfileResponse)(nil), // 153: openshell.v1.ProviderProfileResponse - (*ListProviderProfilesResponse)(nil), // 154: openshell.v1.ListProviderProfilesResponse - (*ImportProviderProfilesRequest)(nil), // 155: openshell.v1.ImportProviderProfilesRequest - (*ImportProviderProfilesResponse)(nil), // 156: openshell.v1.ImportProviderProfilesResponse - (*UpdateProviderProfilesRequest)(nil), // 157: openshell.v1.UpdateProviderProfilesRequest - (*UpdateProviderProfilesResponse)(nil), // 158: openshell.v1.UpdateProviderProfilesResponse - (*LintProviderProfilesRequest)(nil), // 159: openshell.v1.LintProviderProfilesRequest - (*LintProviderProfilesResponse)(nil), // 160: openshell.v1.LintProviderProfilesResponse - (*DeleteProviderResponse)(nil), // 161: openshell.v1.DeleteProviderResponse - (*DeleteProviderProfileRequest)(nil), // 162: openshell.v1.DeleteProviderProfileRequest - (*DeleteProviderProfileResponse)(nil), // 163: openshell.v1.DeleteProviderProfileResponse - (*GetSandboxProviderEnvironmentRequest)(nil), // 164: openshell.v1.GetSandboxProviderEnvironmentRequest - (*StaticCredentialEndpointBinding)(nil), // 165: openshell.v1.StaticCredentialEndpointBinding - (*StaticCredentialBinding)(nil), // 166: openshell.v1.StaticCredentialBinding - (*GetSandboxProviderEnvironmentResponse)(nil), // 167: openshell.v1.GetSandboxProviderEnvironmentResponse - (*ProviderEnvironmentValue)(nil), // 168: openshell.v1.ProviderEnvironmentValue - (*ProviderEnvironmentSnapshot)(nil), // 169: openshell.v1.ProviderEnvironmentSnapshot - (*ExchangeProviderSubjectTokenRequest)(nil), // 170: openshell.v1.ExchangeProviderSubjectTokenRequest - (*ExchangeProviderSubjectTokenResponse)(nil), // 171: openshell.v1.ExchangeProviderSubjectTokenResponse - (*UpdateConfigRequest)(nil), // 172: openshell.v1.UpdateConfigRequest - (*PolicyMergeOperation)(nil), // 173: openshell.v1.PolicyMergeOperation - (*AddNetworkRule)(nil), // 174: openshell.v1.AddNetworkRule - (*RemoveNetworkEndpoint)(nil), // 175: openshell.v1.RemoveNetworkEndpoint - (*RemoveNetworkRule)(nil), // 176: openshell.v1.RemoveNetworkRule - (*L7RuleTarget)(nil), // 177: openshell.v1.L7RuleTarget - (*AddDenyRules)(nil), // 178: openshell.v1.AddDenyRules - (*AddAllowRules)(nil), // 179: openshell.v1.AddAllowRules - (*RemoveNetworkBinary)(nil), // 180: openshell.v1.RemoveNetworkBinary - (*UpdateConfigResponse)(nil), // 181: openshell.v1.UpdateConfigResponse - (*GetSandboxPolicyStatusRequest)(nil), // 182: openshell.v1.GetSandboxPolicyStatusRequest - (*GetSandboxPolicyStatusResponse)(nil), // 183: openshell.v1.GetSandboxPolicyStatusResponse - (*ListSandboxPoliciesRequest)(nil), // 184: openshell.v1.ListSandboxPoliciesRequest - (*ListSandboxPoliciesResponse)(nil), // 185: openshell.v1.ListSandboxPoliciesResponse - (*ReportPolicyStatusRequest)(nil), // 186: openshell.v1.ReportPolicyStatusRequest - (*ReportPolicyStatusResponse)(nil), // 187: openshell.v1.ReportPolicyStatusResponse - (*SandboxConfigurationAdmission)(nil), // 188: openshell.v1.SandboxConfigurationAdmission - (*ReportSandboxConfigurationRequest)(nil), // 189: openshell.v1.ReportSandboxConfigurationRequest - (*ReportSandboxConfigurationResponse)(nil), // 190: openshell.v1.ReportSandboxConfigurationResponse - (*SandboxPolicyRevision)(nil), // 191: openshell.v1.SandboxPolicyRevision - (*GetSandboxLogsRequest)(nil), // 192: openshell.v1.GetSandboxLogsRequest - (*PushSandboxLogsRequest)(nil), // 193: openshell.v1.PushSandboxLogsRequest - (*PushSandboxLogsResponse)(nil), // 194: openshell.v1.PushSandboxLogsResponse - (*GetSandboxLogsResponse)(nil), // 195: openshell.v1.GetSandboxLogsResponse - (*SupervisorMessage)(nil), // 196: openshell.v1.SupervisorMessage - (*GatewayMessage)(nil), // 197: openshell.v1.GatewayMessage - (*SupervisorHello)(nil), // 198: openshell.v1.SupervisorHello - (*SessionAccepted)(nil), // 199: openshell.v1.SessionAccepted - (*ConfigUpdate)(nil), // 200: openshell.v1.ConfigUpdate - (*ConfigApplyFailure)(nil), // 201: openshell.v1.ConfigApplyFailure - (*ConfigUpdateResult)(nil), // 202: openshell.v1.ConfigUpdateResult - (*PolicyLoadResult)(nil), // 203: openshell.v1.PolicyLoadResult - (*SessionRejected)(nil), // 204: openshell.v1.SessionRejected - (*SessionRedirect)(nil), // 205: openshell.v1.SessionRedirect - (*SupervisorHeartbeat)(nil), // 206: openshell.v1.SupervisorHeartbeat - (*GatewayHeartbeat)(nil), // 207: openshell.v1.GatewayHeartbeat - (*ReportMainProcessExitRequest)(nil), // 208: openshell.v1.ReportMainProcessExitRequest - (*ReportMainProcessExitResponse)(nil), // 209: openshell.v1.ReportMainProcessExitResponse - (*FinalizeMainProcessExitRequest)(nil), // 210: openshell.v1.FinalizeMainProcessExitRequest - (*FinalizeMainProcessExitResponse)(nil), // 211: openshell.v1.FinalizeMainProcessExitResponse - (*RelayOpen)(nil), // 212: openshell.v1.RelayOpen - (*SshRelayTarget)(nil), // 213: openshell.v1.SshRelayTarget - (*TcpRelayTarget)(nil), // 214: openshell.v1.TcpRelayTarget - (*RelayInit)(nil), // 215: openshell.v1.RelayInit - (*RelayFrame)(nil), // 216: openshell.v1.RelayFrame - (*PeerRelayInit)(nil), // 217: openshell.v1.PeerRelayInit - (*PeerRelayFrame)(nil), // 218: openshell.v1.PeerRelayFrame - (*PeerConfigSandboxTarget)(nil), // 219: openshell.v1.PeerConfigSandboxTarget - (*PeerConfigProviderTarget)(nil), // 220: openshell.v1.PeerConfigProviderTarget - (*PeerNotifyConfigUpdateRequest)(nil), // 221: openshell.v1.PeerNotifyConfigUpdateRequest - (*PeerNotifyConfigUpdateResponse)(nil), // 222: openshell.v1.PeerNotifyConfigUpdateResponse - (*RelayOpenResult)(nil), // 223: openshell.v1.RelayOpenResult - (*RelayClose)(nil), // 224: openshell.v1.RelayClose - (*L7RequestSample)(nil), // 225: openshell.v1.L7RequestSample - (*DenialSummary)(nil), // 226: openshell.v1.DenialSummary - (*DenialGroupCount)(nil), // 227: openshell.v1.DenialGroupCount - (*NetworkActivitySummary)(nil), // 228: openshell.v1.NetworkActivitySummary - (*PolicyChunk)(nil), // 229: openshell.v1.PolicyChunk - (*DraftPolicyUpdate)(nil), // 230: openshell.v1.DraftPolicyUpdate - (*SubmitPolicyAnalysisRequest)(nil), // 231: openshell.v1.SubmitPolicyAnalysisRequest - (*SubmitPolicyAnalysisResponse)(nil), // 232: openshell.v1.SubmitPolicyAnalysisResponse - (*GetDraftPolicyRequest)(nil), // 233: openshell.v1.GetDraftPolicyRequest - (*GetDraftPolicyResponse)(nil), // 234: openshell.v1.GetDraftPolicyResponse - (*ApproveDraftChunkRequest)(nil), // 235: openshell.v1.ApproveDraftChunkRequest - (*ApproveDraftChunkResponse)(nil), // 236: openshell.v1.ApproveDraftChunkResponse - (*RejectDraftChunkRequest)(nil), // 237: openshell.v1.RejectDraftChunkRequest - (*RejectDraftChunkResponse)(nil), // 238: openshell.v1.RejectDraftChunkResponse - (*DraftChunkApproval)(nil), // 239: openshell.v1.DraftChunkApproval - (*ApproveAllDraftChunksRequest)(nil), // 240: openshell.v1.ApproveAllDraftChunksRequest - (*ApproveAllDraftChunksResponse)(nil), // 241: openshell.v1.ApproveAllDraftChunksResponse - (*EditDraftChunkRequest)(nil), // 242: openshell.v1.EditDraftChunkRequest - (*EditDraftChunkResponse)(nil), // 243: openshell.v1.EditDraftChunkResponse - (*UndoDraftChunkRequest)(nil), // 244: openshell.v1.UndoDraftChunkRequest - (*UndoDraftChunkResponse)(nil), // 245: openshell.v1.UndoDraftChunkResponse - (*ClearDraftChunksRequest)(nil), // 246: openshell.v1.ClearDraftChunksRequest - (*ClearDraftChunksResponse)(nil), // 247: openshell.v1.ClearDraftChunksResponse - (*GetDraftHistoryRequest)(nil), // 248: openshell.v1.GetDraftHistoryRequest - (*DraftHistoryEntry)(nil), // 249: openshell.v1.DraftHistoryEntry - (*GetDraftHistoryResponse)(nil), // 250: openshell.v1.GetDraftHistoryResponse - (*CreateWorkspaceRequest)(nil), // 251: openshell.v1.CreateWorkspaceRequest - (*CreateWorkspaceResponse)(nil), // 252: openshell.v1.CreateWorkspaceResponse - (*GetWorkspaceRequest)(nil), // 253: openshell.v1.GetWorkspaceRequest - (*GetWorkspaceResponse)(nil), // 254: openshell.v1.GetWorkspaceResponse - (*ListWorkspacesRequest)(nil), // 255: openshell.v1.ListWorkspacesRequest - (*ListWorkspacesResponse)(nil), // 256: openshell.v1.ListWorkspacesResponse - (*DeleteWorkspaceRequest)(nil), // 257: openshell.v1.DeleteWorkspaceRequest - (*DeleteWorkspaceResponse)(nil), // 258: openshell.v1.DeleteWorkspaceResponse - (*WorkspaceMember)(nil), // 259: openshell.v1.WorkspaceMember - (*AddWorkspaceMemberRequest)(nil), // 260: openshell.v1.AddWorkspaceMemberRequest - (*AddWorkspaceMemberResponse)(nil), // 261: openshell.v1.AddWorkspaceMemberResponse - (*RemoveWorkspaceMemberRequest)(nil), // 262: openshell.v1.RemoveWorkspaceMemberRequest - (*RemoveWorkspaceMemberResponse)(nil), // 263: openshell.v1.RemoveWorkspaceMemberResponse - (*ListWorkspaceMembersRequest)(nil), // 264: openshell.v1.ListWorkspaceMembersRequest - (*ListWorkspaceMembersResponse)(nil), // 265: openshell.v1.ListWorkspaceMembersResponse - (*ExtensionServiceCredential)(nil), // 266: openshell.v1.ExtensionServiceCredential - (*EndpointObservation)(nil), // 267: openshell.v1.EndpointObservation - (*ReportEndpointStatusRequest)(nil), // 268: openshell.v1.ReportEndpointStatusRequest - (*TokenGrantStatus)(nil), // 269: openshell.v1.TokenGrantStatus - (*TokenGrantFailureObservation)(nil), // 270: openshell.v1.TokenGrantFailureObservation - (*ReportEndpointStatusResponse)(nil), // 271: openshell.v1.ReportEndpointStatusResponse - (*EndpointStatus)(nil), // 272: openshell.v1.EndpointStatus - (*SandboxProvisioning)(nil), // 273: openshell.v1.SandboxProvisioning - (*SandboxServiceExposure)(nil), // 274: openshell.v1.SandboxServiceExposure - (*EndpointFailure)(nil), // 275: openshell.v1.EndpointFailure - nil, // 276: openshell.v1.SandboxSpec.EnvironmentEntry - nil, // 277: openshell.v1.SandboxTemplate.LabelsEntry - nil, // 278: openshell.v1.SandboxTemplate.AnnotationsEntry - nil, // 279: openshell.v1.SandboxTemplate.EnvironmentEntry - nil, // 280: openshell.v1.SandboxWorkloadConfig.EnvironmentEntry - nil, // 281: openshell.v1.PlatformEvent.MetadataEntry - nil, // 282: openshell.v1.CreateSandboxRequest.LabelsEntry - nil, // 283: openshell.v1.CreateSandboxRequest.AnnotationsEntry - nil, // 284: openshell.v1.SandboxMapUpdate.ValuesEntry - nil, // 285: openshell.v1.SandboxResponse.ServiceUrlsEntry - nil, // 286: openshell.v1.ExecSandboxRequest.EnvironmentEntry - nil, // 287: openshell.v1.SandboxLogLine.FieldsEntry - nil, // 288: openshell.v1.GetProviderCredentialsResponse.CredentialsEntry - nil, // 289: openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry - nil, // 290: openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - nil, // 291: openshell.v1.ProviderProfile.AnnotationsEntry - nil, // 292: openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - nil, // 293: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry - nil, // 294: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - nil, // 295: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - nil, // 296: openshell.v1.GetSandboxProviderEnvironmentResponse.FilesEntry - nil, // 297: openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntry - nil, // 298: openshell.v1.ProviderEnvironmentSnapshot.FilesEntry - nil, // 299: openshell.v1.UpdateConfigRequest.AnnotationsEntry - nil, // 300: openshell.v1.UpdateConfigResponse.AnnotationsEntry - nil, // 301: openshell.v1.SandboxPolicyRevision.ProvenanceEntry - nil, // 302: openshell.v1.CreateWorkspaceRequest.LabelsEntry - (*timestamppb.Timestamp)(nil), // 303: google.protobuf.Timestamp - (*datamodelv1.ObjectMeta)(nil), // 304: openshell.datamodel.v1.ObjectMeta - (*sandboxv1.SandboxPolicy)(nil), // 305: openshell.sandbox.v1.SandboxPolicy - (*structpb.Struct)(nil), // 306: google.protobuf.Struct - (*durationpb.Duration)(nil), // 307: google.protobuf.Duration - (*datamodelv1.WorkspaceSelector)(nil), // 308: openshell.datamodel.v1.WorkspaceSelector - (*datamodelv1.Provider)(nil), // 309: openshell.datamodel.v1.Provider - (sandboxv1.PolicySource)(0), // 310: openshell.sandbox.v1.PolicySource - (*sandboxv1.NetworkEndpoint)(nil), // 311: openshell.sandbox.v1.NetworkEndpoint - (*sandboxv1.NetworkBinary)(nil), // 312: openshell.sandbox.v1.NetworkBinary - (*sandboxv1.SettingValue)(nil), // 313: openshell.sandbox.v1.SettingValue - (*sandboxv1.NetworkPolicyRule)(nil), // 314: openshell.sandbox.v1.NetworkPolicyRule - (*sandboxv1.L7DenyRule)(nil), // 315: openshell.sandbox.v1.L7DenyRule - (*sandboxv1.L7Rule)(nil), // 316: openshell.sandbox.v1.L7Rule - (*sandboxv1.SandboxConfigSnapshot)(nil), // 317: openshell.sandbox.v1.SandboxConfigSnapshot - (*datamodelv1.Workspace)(nil), // 318: openshell.datamodel.v1.Workspace - (*sandboxv1.GetSandboxConfigRequest)(nil), // 319: openshell.sandbox.v1.GetSandboxConfigRequest - (*sandboxv1.GetGatewayConfigRequest)(nil), // 320: openshell.sandbox.v1.GetGatewayConfigRequest - (*sandboxv1.GetSandboxConfigResponse)(nil), // 321: openshell.sandbox.v1.GetSandboxConfigResponse - (*sandboxv1.GetGatewayConfigResponse)(nil), // 322: openshell.sandbox.v1.GetGatewayConfigResponse -} -var file_openshell_proto_depIdxs = []int32{ - 303, // 0: openshell.v1.IssueSandboxTokenResponse.expiration_time:type_name -> google.protobuf.Timestamp - 303, // 1: openshell.v1.RefreshSandboxTokenResponse.expiration_time:type_name -> google.protobuf.Timestamp - 266, // 2: openshell.v1.RefreshSandboxTokenResponse.extension_credentials:type_name -> openshell.v1.ExtensionServiceCredential - 303, // 3: openshell.v1.RefreshSandboxTokenResponse.sandbox_expiration_time:type_name -> google.protobuf.Timestamp - 14, // 4: openshell.v1.HealthResponse.status:type_name -> openshell.v1.ServiceStatus - 14, // 5: openshell.v1.GetGatewayInfoResponse.status:type_name -> openshell.v1.ServiceStatus - 33, // 6: openshell.v1.GetGatewayInfoResponse.compute_drivers:type_name -> openshell.v1.ComputeDriverInfo - 32, // 7: openshell.v1.GetGatewayInfoResponse.extensions:type_name -> openshell.v1.NegotiatedExtensionInfo - 0, // 8: openshell.v1.NegotiatedExtensionInfo.kind:type_name -> openshell.v1.ExtensionKind - 34, // 9: openshell.v1.ComputeDriverInfo.capabilities:type_name -> openshell.v1.ComputeDriverCapabilities - 35, // 10: openshell.v1.ComputeDriverCapabilities.resource_capabilities:type_name -> openshell.v1.ResourceCapabilities - 36, // 11: openshell.v1.ResourceCapabilities.cpu:type_name -> openshell.v1.CpuResourceCapabilities - 37, // 12: openshell.v1.ResourceCapabilities.memory:type_name -> openshell.v1.MemoryResourceCapabilities - 38, // 13: openshell.v1.ResourceCapabilities.gpu:type_name -> openshell.v1.GpuResourceCapabilities - 304, // 14: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 40, // 15: openshell.v1.Sandbox.spec:type_name -> openshell.v1.SandboxSpec - 51, // 16: openshell.v1.Sandbox.status:type_name -> openshell.v1.SandboxStatus - 50, // 17: openshell.v1.Sandbox.created_from_workload_template:type_name -> openshell.v1.SandboxWorkloadTemplateProvenance - 276, // 18: openshell.v1.SandboxSpec.environment:type_name -> openshell.v1.SandboxSpec.EnvironmentEntry - 43, // 19: openshell.v1.SandboxSpec.template:type_name -> openshell.v1.SandboxTemplate - 305, // 20: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 41, // 21: openshell.v1.SandboxSpec.resource_requirements:type_name -> openshell.v1.ResourceRequirements - 17, // 22: openshell.v1.SandboxSpec.restart_policy:type_name -> openshell.v1.SandboxRestartPolicy - 42, // 23: openshell.v1.ResourceRequirements.gpu:type_name -> openshell.v1.GpuResourceRequirements - 277, // 24: openshell.v1.SandboxTemplate.labels:type_name -> openshell.v1.SandboxTemplate.LabelsEntry - 278, // 25: openshell.v1.SandboxTemplate.annotations:type_name -> openshell.v1.SandboxTemplate.AnnotationsEntry - 279, // 26: openshell.v1.SandboxTemplate.environment:type_name -> openshell.v1.SandboxTemplate.EnvironmentEntry - 306, // 27: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct - 306, // 28: openshell.v1.SandboxTemplate.driver_config:type_name -> google.protobuf.Struct - 304, // 29: openshell.v1.SandboxWorkloadTemplate.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 45, // 30: openshell.v1.SandboxWorkloadTemplate.spec:type_name -> openshell.v1.SandboxWorkloadTemplateSpec - 46, // 31: openshell.v1.SandboxWorkloadTemplateSpec.workload:type_name -> openshell.v1.SandboxWorkloadConfig - 306, // 32: openshell.v1.SandboxWorkloadTemplateSpec.driver_config:type_name -> google.protobuf.Struct - 48, // 33: openshell.v1.SandboxWorkloadTemplateSpec.desired_service_level:type_name -> openshell.v1.SandboxServiceLevel - 280, // 34: openshell.v1.SandboxWorkloadConfig.environment:type_name -> openshell.v1.SandboxWorkloadConfig.EnvironmentEntry - 47, // 35: openshell.v1.SandboxWorkloadConfig.resources:type_name -> openshell.v1.SandboxResources - 42, // 36: openshell.v1.SandboxResources.gpu:type_name -> openshell.v1.GpuResourceRequirements - 49, // 37: openshell.v1.SandboxServiceLevel.startup:type_name -> openshell.v1.SandboxStartup - 307, // 38: openshell.v1.SandboxStartup.ready_within:type_name -> google.protobuf.Duration - 52, // 39: openshell.v1.SandboxStatus.conditions:type_name -> openshell.v1.SandboxCondition - 1, // 40: openshell.v1.SandboxStatus.phase:type_name -> openshell.v1.SandboxPhase - 272, // 41: openshell.v1.SandboxStatus.endpoint_statuses:type_name -> openshell.v1.EndpointStatus - 188, // 42: openshell.v1.SandboxStatus.configuration_admission:type_name -> openshell.v1.SandboxConfigurationAdmission - 273, // 43: openshell.v1.SandboxStatus.provisioning:type_name -> openshell.v1.SandboxProvisioning - 303, // 44: openshell.v1.SandboxStatus.next_restart_time:type_name -> google.protobuf.Timestamp - 303, // 45: openshell.v1.SandboxStatus.main_process_started_time:type_name -> google.protobuf.Timestamp - 270, // 46: openshell.v1.SandboxStatus.last_token_grant_failure:type_name -> openshell.v1.TokenGrantFailureObservation - 303, // 47: openshell.v1.SandboxCondition.transition_time:type_name -> google.protobuf.Timestamp - 303, // 48: openshell.v1.PlatformEvent.event_time:type_name -> google.protobuf.Timestamp - 281, // 49: openshell.v1.PlatformEvent.metadata:type_name -> openshell.v1.PlatformEvent.MetadataEntry - 308, // 50: openshell.v1.CreateSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 40, // 51: openshell.v1.CreateSandboxRequest.spec:type_name -> openshell.v1.SandboxSpec - 282, // 52: openshell.v1.CreateSandboxRequest.labels:type_name -> openshell.v1.CreateSandboxRequest.LabelsEntry - 283, // 53: openshell.v1.CreateSandboxRequest.annotations:type_name -> openshell.v1.CreateSandboxRequest.AnnotationsEntry - 274, // 54: openshell.v1.CreateSandboxRequest.service_exposures:type_name -> openshell.v1.SandboxServiceExposure - 308, // 55: openshell.v1.UpdateSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 56, // 56: openshell.v1.UpdateSandboxRequest.command:type_name -> openshell.v1.SandboxCommandUpdate - 57, // 57: openshell.v1.UpdateSandboxRequest.environment:type_name -> openshell.v1.SandboxMapUpdate - 57, // 58: openshell.v1.UpdateSandboxRequest.labels:type_name -> openshell.v1.SandboxMapUpdate - 57, // 59: openshell.v1.UpdateSandboxRequest.annotations:type_name -> openshell.v1.SandboxMapUpdate - 284, // 60: openshell.v1.SandboxMapUpdate.values:type_name -> openshell.v1.SandboxMapUpdate.ValuesEntry - 308, // 61: openshell.v1.CreateSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 44, // 62: openshell.v1.CreateSandboxTemplateRequest.template:type_name -> openshell.v1.SandboxWorkloadTemplate - 308, // 63: openshell.v1.GetSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 64: openshell.v1.ListSandboxTemplatesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 65: openshell.v1.DeleteSandboxTemplateRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 44, // 66: openshell.v1.SandboxTemplateResponse.template:type_name -> openshell.v1.SandboxWorkloadTemplate - 44, // 67: openshell.v1.ListSandboxTemplatesResponse.templates:type_name -> openshell.v1.SandboxWorkloadTemplate - 18, // 68: openshell.v1.DeleteSandboxTemplateResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 308, // 69: openshell.v1.BeginRootfsTarStagingRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 303, // 70: openshell.v1.BeginRootfsTarStagingResponse.expiration_time:type_name -> google.protobuf.Timestamp - 308, // 71: openshell.v1.GetSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 72: openshell.v1.ListSandboxesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 73: openshell.v1.ListSandboxProvidersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 74: openshell.v1.AttachSandboxProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 75: openshell.v1.DetachSandboxProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 76: openshell.v1.DeleteSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 77: openshell.v1.StopSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 78: openshell.v1.StartSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 79: openshell.v1.SuspendSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 80: openshell.v1.ResumeSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 39, // 81: openshell.v1.SandboxResponse.sandbox:type_name -> openshell.v1.Sandbox - 285, // 82: openshell.v1.SandboxResponse.service_urls:type_name -> openshell.v1.SandboxResponse.ServiceUrlsEntry - 39, // 83: openshell.v1.ListSandboxesResponse.sandboxes:type_name -> openshell.v1.Sandbox - 309, // 84: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 39, // 85: openshell.v1.AttachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 86, // 86: openshell.v1.AttachSandboxProviderResponse.receipt:type_name -> openshell.v1.ProviderMutationReceipt - 39, // 87: openshell.v1.DetachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 86, // 88: openshell.v1.DetachSandboxProviderResponse.receipt:type_name -> openshell.v1.ProviderMutationReceipt - 84, // 89: openshell.v1.ConfigSnapshotRevision.sandbox_config:type_name -> openshell.v1.SandboxConfigRevision - 82, // 90: openshell.v1.ConfigSnapshotRevision.provider_target:type_name -> openshell.v1.ProviderDesiredIdentity - 310, // 91: openshell.v1.SandboxConfigRevision.policy_source:type_name -> openshell.sandbox.v1.PolicySource - 5, // 92: openshell.v1.ConfigUpdateOperation.component:type_name -> openshell.v1.ConfigComponent - 83, // 93: openshell.v1.ConfigUpdateOperation.target_revision:type_name -> openshell.v1.ConfigSnapshotRevision - 7, // 94: openshell.v1.ConfigUpdateOperation.state:type_name -> openshell.v1.ConfigUpdateOperationState - 6, // 95: openshell.v1.ConfigUpdateOperation.outcome:type_name -> openshell.v1.ConfigApplyOutcome - 303, // 96: openshell.v1.ConfigUpdateOperation.created_time:type_name -> google.protobuf.Timestamp - 303, // 97: openshell.v1.ConfigUpdateOperation.updated_time:type_name -> google.protobuf.Timestamp - 303, // 98: openshell.v1.ConfigUpdateOperation.completed_time:type_name -> google.protobuf.Timestamp - 2, // 99: openshell.v1.ProviderMutationReceipt.kind:type_name -> openshell.v1.ProviderMutationKind - 82, // 100: openshell.v1.ProviderMutationReceipt.desired:type_name -> openshell.v1.ProviderDesiredIdentity - 303, // 101: openshell.v1.ProviderMutationReceipt.persisted_time:type_name -> google.protobuf.Timestamp - 4, // 102: openshell.v1.ProviderReadinessObservation.reason:type_name -> openshell.v1.ProviderReadinessReason - 86, // 103: openshell.v1.ProviderReadinessStatus.receipt:type_name -> openshell.v1.ProviderMutationReceipt - 3, // 104: openshell.v1.ProviderReadinessStatus.state:type_name -> openshell.v1.ProviderReadinessState - 4, // 105: openshell.v1.ProviderReadinessStatus.reason:type_name -> openshell.v1.ProviderReadinessReason - 87, // 106: openshell.v1.ProviderReadinessStatus.observed:type_name -> openshell.v1.ProviderReadinessObservation - 303, // 107: openshell.v1.ProviderReadinessStatus.observed_time:type_name -> google.protobuf.Timestamp - 303, // 108: openshell.v1.ProviderReadinessStatus.evaluated_time:type_name -> google.protobuf.Timestamp - 85, // 109: openshell.v1.ProviderReadinessStatus.operation:type_name -> openshell.v1.ConfigUpdateOperation - 308, // 110: openshell.v1.GetSandboxProviderStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 88, // 111: openshell.v1.GetSandboxProviderStatusResponse.status:type_name -> openshell.v1.ProviderReadinessStatus - 87, // 112: openshell.v1.ReportProviderReadinessRequest.observation:type_name -> openshell.v1.ProviderReadinessObservation - 307, // 113: openshell.v1.ReportProviderReadinessResponse.report_interval:type_name -> google.protobuf.Duration - 307, // 114: openshell.v1.ReportProviderReadinessResponse.observation_ttl:type_name -> google.protobuf.Duration - 18, // 115: openshell.v1.DeleteSandboxResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 308, // 116: openshell.v1.CreateSshSessionRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 303, // 117: openshell.v1.CreateSshSessionResponse.expiration_time:type_name -> google.protobuf.Timestamp - 308, // 118: openshell.v1.ExposeServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 20, // 119: openshell.v1.ExposeServiceRequest.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode - 308, // 120: openshell.v1.GetServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 121: openshell.v1.ListServicesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 103, // 122: openshell.v1.ListServicesResponse.services:type_name -> openshell.v1.ServiceEndpointResponse - 308, // 123: openshell.v1.DeleteServiceRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 18, // 124: openshell.v1.DeleteServiceResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 304, // 125: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 20, // 126: openshell.v1.ServiceEndpoint.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode - 102, // 127: openshell.v1.ServiceEndpointResponse.endpoint:type_name -> openshell.v1.ServiceEndpoint - 18, // 128: openshell.v1.RevokeSshSessionResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 308, // 129: openshell.v1.ExecSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 286, // 130: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry - 307, // 131: openshell.v1.ExecSandboxRequest.execution_timeout:type_name -> google.protobuf.Duration - 107, // 132: openshell.v1.ExecSandboxEvent.stdout:type_name -> openshell.v1.ExecSandboxStdout - 108, // 133: openshell.v1.ExecSandboxEvent.stderr:type_name -> openshell.v1.ExecSandboxStderr - 109, // 134: openshell.v1.ExecSandboxEvent.exit:type_name -> openshell.v1.ExecSandboxExit - 213, // 135: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget - 214, // 136: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget - 111, // 137: openshell.v1.TcpForwardFrame.init:type_name -> openshell.v1.TcpForwardInit - 106, // 138: openshell.v1.ExecSandboxInput.start:type_name -> openshell.v1.ExecSandboxRequest - 114, // 139: openshell.v1.ExecSandboxInput.resize:type_name -> openshell.v1.ExecSandboxWindowResize - 304, // 140: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 303, // 141: openshell.v1.SshSession.expiration_time:type_name -> google.protobuf.Timestamp - 308, // 142: openshell.v1.WatchSandboxRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 303, // 143: openshell.v1.WatchSandboxRequest.since_time:type_name -> google.protobuf.Timestamp - 39, // 144: openshell.v1.SandboxStreamEvent.sandbox:type_name -> openshell.v1.Sandbox - 118, // 145: openshell.v1.SandboxStreamEvent.log:type_name -> openshell.v1.SandboxLogLine - 53, // 146: openshell.v1.SandboxStreamEvent.event:type_name -> openshell.v1.PlatformEvent - 119, // 147: openshell.v1.SandboxStreamEvent.warning:type_name -> openshell.v1.SandboxStreamWarning - 230, // 148: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate - 303, // 149: openshell.v1.SandboxLogLine.event_time:type_name -> google.protobuf.Timestamp - 287, // 150: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry - 308, // 151: openshell.v1.CreateProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 309, // 152: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 308, // 153: openshell.v1.GetProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 154: openshell.v1.GetProviderCredentialsRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 307, // 155: openshell.v1.GetProviderCredentialsRequest.minimum_remaining_lifetime:type_name -> google.protobuf.Duration - 303, // 156: openshell.v1.ProviderCredentialValue.expiration_time:type_name -> google.protobuf.Timestamp - 288, // 157: openshell.v1.GetProviderCredentialsResponse.credentials:type_name -> openshell.v1.GetProviderCredentialsResponse.CredentialsEntry - 308, // 158: openshell.v1.ListProvidersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 159: openshell.v1.UpdateProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 309, // 160: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 289, // 161: openshell.v1.UpdateProviderRequest.credential_expiration_times:type_name -> openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry - 308, // 162: openshell.v1.DeleteProviderRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 309, // 163: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider - 86, // 164: openshell.v1.ProviderResponse.target_receipts:type_name -> openshell.v1.ProviderMutationReceipt - 309, // 165: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 308, // 166: openshell.v1.ListProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 167: openshell.v1.GetProviderProfileRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 151, // 168: openshell.v1.ProviderProfileImportItem.profile:type_name -> openshell.v1.ProviderProfile - 307, // 169: openshell.v1.ProviderCredentialTokenGrant.cache_ttl:type_name -> google.protobuf.Duration - 134, // 170: openshell.v1.ProviderCredentialTokenGrant.audience_overrides:type_name -> openshell.v1.ProviderCredentialTokenGrantAudienceOverride - 8, // 171: openshell.v1.ProviderCredentialTokenGrant.grant_type:type_name -> openshell.v1.ProviderCredentialTokenGrantType - 135, // 172: openshell.v1.ProviderCredentialTokenGrant.subject_token:type_name -> openshell.v1.ProviderCredentialTokenGrantSubjectToken - 140, // 173: openshell.v1.ProviderProfileCredential.refresh:type_name -> openshell.v1.ProviderCredentialRefresh - 136, // 174: openshell.v1.ProviderProfileCredential.token_grant:type_name -> openshell.v1.ProviderCredentialTokenGrant - 9, // 175: openshell.v1.ProviderCredentialRefresh.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 307, // 176: openshell.v1.ProviderCredentialRefresh.refresh_before:type_name -> google.protobuf.Duration - 307, // 177: openshell.v1.ProviderCredentialRefresh.max_lifetime:type_name -> google.protobuf.Duration - 138, // 178: openshell.v1.ProviderCredentialRefresh.material:type_name -> openshell.v1.ProviderCredentialRefreshMaterial - 139, // 179: openshell.v1.ProviderCredentialRefresh.additional_outputs:type_name -> openshell.v1.ProviderCredentialRefreshOutput - 9, // 180: openshell.v1.ProviderCredentialRefreshStatus.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 303, // 181: openshell.v1.ProviderCredentialRefreshStatus.expiration_time:type_name -> google.protobuf.Timestamp - 303, // 182: openshell.v1.ProviderCredentialRefreshStatus.next_refresh_time:type_name -> google.protobuf.Timestamp - 303, // 183: openshell.v1.ProviderCredentialRefreshStatus.last_refresh_time:type_name -> google.protobuf.Timestamp - 16, // 184: openshell.v1.ProviderCredentialRefreshStatus.recovery_action:type_name -> openshell.v1.ProviderCredentialRefreshRecoveryAction - 303, // 185: openshell.v1.ProviderCredentialRefreshStatus.last_error_time:type_name -> google.protobuf.Timestamp - 308, // 186: openshell.v1.GetProviderRefreshStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 141, // 187: openshell.v1.GetProviderRefreshStatusResponse.credentials:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 308, // 188: openshell.v1.ConfigureProviderRefreshRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 9, // 189: openshell.v1.ConfigureProviderRefreshRequest.strategy:type_name -> openshell.v1.ProviderCredentialRefreshStrategy - 290, // 190: openshell.v1.ConfigureProviderRefreshRequest.material:type_name -> openshell.v1.ConfigureProviderRefreshRequest.MaterialEntry - 303, // 191: openshell.v1.ConfigureProviderRefreshRequest.expiration_time:type_name -> google.protobuf.Timestamp - 141, // 192: openshell.v1.ConfigureProviderRefreshResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 308, // 193: openshell.v1.RotateProviderCredentialRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 141, // 194: openshell.v1.RotateProviderCredentialResponse.status:type_name -> openshell.v1.ProviderCredentialRefreshStatus - 308, // 195: openshell.v1.DeleteProviderRefreshRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 18, // 196: openshell.v1.DeleteProviderRefreshResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 10, // 197: openshell.v1.ProviderProfile.category:type_name -> openshell.v1.ProviderProfileCategory - 137, // 198: openshell.v1.ProviderProfile.credentials:type_name -> openshell.v1.ProviderProfileCredential - 311, // 199: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint - 312, // 200: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary - 142, // 201: openshell.v1.ProviderProfile.discovery:type_name -> openshell.v1.ProviderProfileDiscovery - 291, // 202: openshell.v1.ProviderProfile.annotations:type_name -> openshell.v1.ProviderProfile.AnnotationsEntry - 152, // 203: openshell.v1.ProviderProfile.files:type_name -> openshell.v1.ProviderProfileFile - 151, // 204: openshell.v1.ProviderProfileResponse.profile:type_name -> openshell.v1.ProviderProfile - 151, // 205: openshell.v1.ListProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 308, // 206: openshell.v1.ImportProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 132, // 207: openshell.v1.ImportProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 133, // 208: openshell.v1.ImportProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 151, // 209: openshell.v1.ImportProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 308, // 210: openshell.v1.UpdateProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 132, // 211: openshell.v1.UpdateProviderProfilesRequest.profile:type_name -> openshell.v1.ProviderProfileImportItem - 133, // 212: openshell.v1.UpdateProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 151, // 213: openshell.v1.UpdateProviderProfilesResponse.profile:type_name -> openshell.v1.ProviderProfile - 308, // 214: openshell.v1.LintProviderProfilesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 132, // 215: openshell.v1.LintProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 133, // 216: openshell.v1.LintProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 18, // 217: openshell.v1.DeleteProviderResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 308, // 218: openshell.v1.DeleteProviderProfileRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 18, // 219: openshell.v1.DeleteProviderProfileResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 165, // 220: openshell.v1.StaticCredentialBinding.endpoints:type_name -> openshell.v1.StaticCredentialEndpointBinding - 292, // 221: openshell.v1.GetSandboxProviderEnvironmentResponse.environment:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - 293, // 222: openshell.v1.GetSandboxProviderEnvironmentResponse.credential_expiration_times:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry - 294, // 223: openshell.v1.GetSandboxProviderEnvironmentResponse.dynamic_credentials:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry - 295, // 224: openshell.v1.GetSandboxProviderEnvironmentResponse.static_credential_bindings:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry - 4, // 225: openshell.v1.GetSandboxProviderEnvironmentResponse.readiness_reason:type_name -> openshell.v1.ProviderReadinessReason - 296, // 226: openshell.v1.GetSandboxProviderEnvironmentResponse.files:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.FilesEntry - 11, // 227: openshell.v1.ProviderEnvironmentValue.classification:type_name -> openshell.v1.ProviderEnvironmentValueClassification - 166, // 228: openshell.v1.ProviderEnvironmentValue.static_credential_binding:type_name -> openshell.v1.StaticCredentialBinding - 303, // 229: openshell.v1.ProviderEnvironmentValue.expiration_time:type_name -> google.protobuf.Timestamp - 168, // 230: openshell.v1.ProviderEnvironmentSnapshot.values:type_name -> openshell.v1.ProviderEnvironmentValue - 297, // 231: openshell.v1.ProviderEnvironmentSnapshot.dynamic_credentials:type_name -> openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntry - 4, // 232: openshell.v1.ProviderEnvironmentSnapshot.readiness_reason:type_name -> openshell.v1.ProviderReadinessReason - 298, // 233: openshell.v1.ProviderEnvironmentSnapshot.files:type_name -> openshell.v1.ProviderEnvironmentSnapshot.FilesEntry - 307, // 234: openshell.v1.ExchangeProviderSubjectTokenResponse.expires_after:type_name -> google.protobuf.Duration - 308, // 235: openshell.v1.UpdateConfigRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 305, // 236: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 313, // 237: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue - 173, // 238: openshell.v1.UpdateConfigRequest.merge_operations:type_name -> openshell.v1.PolicyMergeOperation - 299, // 239: openshell.v1.UpdateConfigRequest.annotations:type_name -> openshell.v1.UpdateConfigRequest.AnnotationsEntry - 174, // 240: openshell.v1.PolicyMergeOperation.add_rule:type_name -> openshell.v1.AddNetworkRule - 175, // 241: openshell.v1.PolicyMergeOperation.remove_endpoint:type_name -> openshell.v1.RemoveNetworkEndpoint - 176, // 242: openshell.v1.PolicyMergeOperation.remove_rule:type_name -> openshell.v1.RemoveNetworkRule - 178, // 243: openshell.v1.PolicyMergeOperation.add_deny_rules:type_name -> openshell.v1.AddDenyRules - 179, // 244: openshell.v1.PolicyMergeOperation.add_allow_rules:type_name -> openshell.v1.AddAllowRules - 180, // 245: openshell.v1.PolicyMergeOperation.remove_binary:type_name -> openshell.v1.RemoveNetworkBinary - 314, // 246: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 312, // 247: openshell.v1.L7RuleTarget.binaries:type_name -> openshell.sandbox.v1.NetworkBinary - 315, // 248: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule - 177, // 249: openshell.v1.AddDenyRules.target:type_name -> openshell.v1.L7RuleTarget - 316, // 250: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule - 177, // 251: openshell.v1.AddAllowRules.target:type_name -> openshell.v1.L7RuleTarget - 300, // 252: openshell.v1.UpdateConfigResponse.annotations:type_name -> openshell.v1.UpdateConfigResponse.AnnotationsEntry - 308, // 253: openshell.v1.GetSandboxPolicyStatusRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 191, // 254: openshell.v1.GetSandboxPolicyStatusResponse.revision:type_name -> openshell.v1.SandboxPolicyRevision - 308, // 255: openshell.v1.ListSandboxPoliciesRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 191, // 256: openshell.v1.ListSandboxPoliciesResponse.revisions:type_name -> openshell.v1.SandboxPolicyRevision - 13, // 257: openshell.v1.ReportPolicyStatusRequest.status:type_name -> openshell.v1.PolicyStatus - 12, // 258: openshell.v1.SandboxConfigurationAdmission.state:type_name -> openshell.v1.ConfigurationAdmissionState - 188, // 259: openshell.v1.ReportSandboxConfigurationRequest.admission:type_name -> openshell.v1.SandboxConfigurationAdmission - 13, // 260: openshell.v1.SandboxPolicyRevision.status:type_name -> openshell.v1.PolicyStatus - 303, // 261: openshell.v1.SandboxPolicyRevision.created_time:type_name -> google.protobuf.Timestamp - 303, // 262: openshell.v1.SandboxPolicyRevision.loaded_time:type_name -> google.protobuf.Timestamp - 305, // 263: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 301, // 264: openshell.v1.SandboxPolicyRevision.provenance:type_name -> openshell.v1.SandboxPolicyRevision.ProvenanceEntry - 308, // 265: openshell.v1.GetSandboxLogsRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 303, // 266: openshell.v1.GetSandboxLogsRequest.since_time:type_name -> google.protobuf.Timestamp - 118, // 267: openshell.v1.PushSandboxLogsRequest.logs:type_name -> openshell.v1.SandboxLogLine - 118, // 268: openshell.v1.GetSandboxLogsResponse.logs:type_name -> openshell.v1.SandboxLogLine - 198, // 269: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello - 206, // 270: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat - 223, // 271: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult - 224, // 272: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose - 202, // 273: openshell.v1.SupervisorMessage.config_update_result:type_name -> openshell.v1.ConfigUpdateResult - 199, // 274: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted - 204, // 275: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected - 207, // 276: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat - 212, // 277: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen - 224, // 278: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose - 205, // 279: openshell.v1.GatewayMessage.session_redirect:type_name -> openshell.v1.SessionRedirect - 200, // 280: openshell.v1.GatewayMessage.config_update:type_name -> openshell.v1.ConfigUpdate - 200, // 281: openshell.v1.SessionAccepted.bootstrap:type_name -> openshell.v1.ConfigUpdate - 307, // 282: openshell.v1.SessionAccepted.heartbeat_interval:type_name -> google.protobuf.Duration - 317, // 283: openshell.v1.ConfigUpdate.sandbox_config:type_name -> openshell.sandbox.v1.SandboxConfigSnapshot - 169, // 284: openshell.v1.ConfigUpdate.provider_environment:type_name -> openshell.v1.ProviderEnvironmentSnapshot - 6, // 285: openshell.v1.ConfigUpdateResult.outcome:type_name -> openshell.v1.ConfigApplyOutcome - 201, // 286: openshell.v1.ConfigUpdateResult.failure:type_name -> openshell.v1.ConfigApplyFailure - 188, // 287: openshell.v1.ConfigUpdateResult.admission:type_name -> openshell.v1.SandboxConfigurationAdmission - 203, // 288: openshell.v1.ConfigUpdateResult.policy_load:type_name -> openshell.v1.PolicyLoadResult - 213, // 289: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget - 214, // 290: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget - 215, // 291: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit - 212, // 292: openshell.v1.PeerRelayInit.relay_open:type_name -> openshell.v1.RelayOpen - 217, // 293: openshell.v1.PeerRelayFrame.init:type_name -> openshell.v1.PeerRelayInit - 219, // 294: openshell.v1.PeerNotifyConfigUpdateRequest.sandbox:type_name -> openshell.v1.PeerConfigSandboxTarget - 220, // 295: openshell.v1.PeerNotifyConfigUpdateRequest.provider:type_name -> openshell.v1.PeerConfigProviderTarget - 303, // 296: openshell.v1.DenialSummary.first_seen_time:type_name -> google.protobuf.Timestamp - 303, // 297: openshell.v1.DenialSummary.last_seen_time:type_name -> google.protobuf.Timestamp - 225, // 298: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample - 227, // 299: openshell.v1.NetworkActivitySummary.denials_by_group:type_name -> openshell.v1.DenialGroupCount - 314, // 300: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 303, // 301: openshell.v1.PolicyChunk.created_time:type_name -> google.protobuf.Timestamp - 303, // 302: openshell.v1.PolicyChunk.decided_time:type_name -> google.protobuf.Timestamp - 303, // 303: openshell.v1.PolicyChunk.first_seen_time:type_name -> google.protobuf.Timestamp - 303, // 304: openshell.v1.PolicyChunk.last_seen_time:type_name -> google.protobuf.Timestamp - 305, // 305: openshell.v1.PolicyChunk.current_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 305, // 306: openshell.v1.PolicyChunk.candidate_effective_policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 308, // 307: openshell.v1.SubmitPolicyAnalysisRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 226, // 308: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary - 229, // 309: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk - 228, // 310: openshell.v1.SubmitPolicyAnalysisRequest.network_activity_summaries:type_name -> openshell.v1.NetworkActivitySummary - 308, // 311: openshell.v1.GetDraftPolicyRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 229, // 312: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk - 303, // 313: openshell.v1.GetDraftPolicyResponse.last_analyzed_time:type_name -> google.protobuf.Timestamp - 308, // 314: openshell.v1.ApproveDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 315: openshell.v1.RejectDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 316: openshell.v1.ApproveAllDraftChunksRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 239, // 317: openshell.v1.ApproveAllDraftChunksRequest.approvals:type_name -> openshell.v1.DraftChunkApproval - 308, // 318: openshell.v1.EditDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 314, // 319: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 308, // 320: openshell.v1.UndoDraftChunkRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 321: openshell.v1.ClearDraftChunksRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 308, // 322: openshell.v1.GetDraftHistoryRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 303, // 323: openshell.v1.DraftHistoryEntry.event_time:type_name -> google.protobuf.Timestamp - 249, // 324: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry - 302, // 325: openshell.v1.CreateWorkspaceRequest.labels:type_name -> openshell.v1.CreateWorkspaceRequest.LabelsEntry - 318, // 326: openshell.v1.CreateWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 318, // 327: openshell.v1.GetWorkspaceResponse.workspace:type_name -> openshell.datamodel.v1.Workspace - 318, // 328: openshell.v1.ListWorkspacesResponse.workspaces:type_name -> openshell.datamodel.v1.Workspace - 18, // 329: openshell.v1.DeleteWorkspaceResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 304, // 330: openshell.v1.WorkspaceMember.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 15, // 331: openshell.v1.WorkspaceMember.role:type_name -> openshell.v1.WorkspaceRole - 308, // 332: openshell.v1.AddWorkspaceMemberRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 15, // 333: openshell.v1.AddWorkspaceMemberRequest.role:type_name -> openshell.v1.WorkspaceRole - 259, // 334: openshell.v1.AddWorkspaceMemberResponse.member:type_name -> openshell.v1.WorkspaceMember - 308, // 335: openshell.v1.RemoveWorkspaceMemberRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 18, // 336: openshell.v1.RemoveWorkspaceMemberResponse.outcome:type_name -> openshell.v1.DeletionOutcome - 308, // 337: openshell.v1.ListWorkspaceMembersRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 259, // 338: openshell.v1.ListWorkspaceMembersResponse.members:type_name -> openshell.v1.WorkspaceMember - 303, // 339: openshell.v1.ExtensionServiceCredential.expiration_time:type_name -> google.protobuf.Timestamp - 19, // 340: openshell.v1.EndpointObservation.result:type_name -> openshell.v1.EndpointResult - 275, // 341: openshell.v1.EndpointObservation.failure:type_name -> openshell.v1.EndpointFailure - 267, // 342: openshell.v1.ReportEndpointStatusRequest.observations:type_name -> openshell.v1.EndpointObservation - 269, // 343: openshell.v1.ReportEndpointStatusRequest.token_grant_status:type_name -> openshell.v1.TokenGrantStatus - 270, // 344: openshell.v1.TokenGrantStatus.last_failure:type_name -> openshell.v1.TokenGrantFailureObservation - 275, // 345: openshell.v1.TokenGrantFailureObservation.failure:type_name -> openshell.v1.EndpointFailure - 19, // 346: openshell.v1.EndpointStatus.last_result:type_name -> openshell.v1.EndpointResult - 303, // 347: openshell.v1.EndpointStatus.last_reported_time:type_name -> google.protobuf.Timestamp - 275, // 348: openshell.v1.EndpointStatus.last_failure:type_name -> openshell.v1.EndpointFailure - 303, // 349: openshell.v1.SandboxProvisioning.configuration_change_time:type_name -> google.protobuf.Timestamp - 303, // 350: openshell.v1.SandboxProvisioning.first_rejection_time:type_name -> google.protobuf.Timestamp - 303, // 351: openshell.v1.SandboxProvisioning.deadline:type_name -> google.protobuf.Timestamp - 303, // 352: openshell.v1.SandboxProvisioning.timeout_time:type_name -> google.protobuf.Timestamp - 303, // 353: openshell.v1.SandboxProvisioning.cleanup_completed_time:type_name -> google.protobuf.Timestamp - 303, // 354: openshell.v1.SandboxProvisioning.cleanup_retry_time:type_name -> google.protobuf.Timestamp - 303, // 355: openshell.v1.SandboxProvisioning.attachment_change_time:type_name -> google.protobuf.Timestamp - 303, // 356: openshell.v1.SandboxProvisioning.preparation_deadline:type_name -> google.protobuf.Timestamp - 303, // 357: openshell.v1.SandboxProvisioning.admission_start_time:type_name -> google.protobuf.Timestamp - 20, // 358: openshell.v1.SandboxServiceExposure.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode - 21, // 359: openshell.v1.EndpointFailure.domain:type_name -> openshell.v1.EndpointFailureDomain - 16, // 360: openshell.v1.EndpointFailure.recovery_action:type_name -> openshell.v1.ProviderCredentialRefreshRecoveryAction - 123, // 361: openshell.v1.GetProviderCredentialsResponse.CredentialsEntry.value:type_name -> openshell.v1.ProviderCredentialValue - 303, // 362: openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp - 303, // 363: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp - 137, // 364: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential - 166, // 365: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding - 137, // 366: openshell.v1.ProviderEnvironmentSnapshot.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential - 26, // 367: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest - 28, // 368: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest - 30, // 369: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest - 54, // 370: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest - 65, // 371: openshell.v1.OpenShell.BeginRootfsTarStaging:input_type -> openshell.v1.BeginRootfsTarStagingRequest - 67, // 372: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest - 68, // 373: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest - 58, // 374: openshell.v1.OpenShell.CreateSandboxTemplate:input_type -> openshell.v1.CreateSandboxTemplateRequest - 59, // 375: openshell.v1.OpenShell.GetSandboxTemplate:input_type -> openshell.v1.GetSandboxTemplateRequest - 60, // 376: openshell.v1.OpenShell.ListSandboxTemplates:input_type -> openshell.v1.ListSandboxTemplatesRequest - 61, // 377: openshell.v1.OpenShell.DeleteSandboxTemplate:input_type -> openshell.v1.DeleteSandboxTemplateRequest - 69, // 378: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest - 70, // 379: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest - 71, // 380: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest - 89, // 381: openshell.v1.OpenShell.GetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest - 72, // 382: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest - 73, // 383: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest - 74, // 384: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest - 75, // 385: openshell.v1.OpenShell.SuspendSandbox:input_type -> openshell.v1.SuspendSandboxRequest - 76, // 386: openshell.v1.OpenShell.ResumeSandbox:input_type -> openshell.v1.ResumeSandboxRequest - 94, // 387: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest - 96, // 388: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest - 97, // 389: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest - 98, // 390: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest - 100, // 391: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest - 104, // 392: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest - 106, // 393: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest - 112, // 394: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame - 113, // 395: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput - 120, // 396: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest - 121, // 397: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest - 122, // 398: openshell.v1.OpenShell.GetProviderCredentials:input_type -> openshell.v1.GetProviderCredentialsRequest - 125, // 399: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest - 130, // 400: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest - 131, // 401: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest - 155, // 402: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest - 157, // 403: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest - 159, // 404: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest - 126, // 405: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest - 143, // 406: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest - 145, // 407: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest - 147, // 408: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest - 149, // 409: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest - 127, // 410: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest - 162, // 411: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest - 319, // 412: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest - 320, // 413: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest - 172, // 414: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest - 182, // 415: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest - 184, // 416: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest - 186, // 417: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest - 268, // 418: openshell.v1.OpenShell.ReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest - 91, // 419: openshell.v1.OpenShell.ReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest - 189, // 420: openshell.v1.OpenShell.ReportSandboxConfiguration:input_type -> openshell.v1.ReportSandboxConfigurationRequest - 164, // 421: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest - 170, // 422: openshell.v1.OpenShell.ExchangeProviderSubjectToken:input_type -> openshell.v1.ExchangeProviderSubjectTokenRequest - 192, // 423: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest - 193, // 424: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest - 196, // 425: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage - 208, // 426: openshell.v1.OpenShell.ReportMainProcessExit:input_type -> openshell.v1.ReportMainProcessExitRequest - 210, // 427: openshell.v1.OpenShell.FinalizeMainProcessExit:input_type -> openshell.v1.FinalizeMainProcessExitRequest - 216, // 428: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame - 218, // 429: openshell.v1.OpenShell.PeerRelay:input_type -> openshell.v1.PeerRelayFrame - 91, // 430: openshell.v1.OpenShell.PeerReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest - 268, // 431: openshell.v1.OpenShell.PeerReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest - 89, // 432: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest - 221, // 433: openshell.v1.OpenShell.PeerNotifyConfigUpdate:input_type -> openshell.v1.PeerNotifyConfigUpdateRequest - 116, // 434: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest - 231, // 435: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest - 233, // 436: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest - 235, // 437: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest - 237, // 438: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest - 240, // 439: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest - 242, // 440: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest - 244, // 441: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest - 246, // 442: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest - 248, // 443: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest - 22, // 444: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest - 24, // 445: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest - 251, // 446: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest - 253, // 447: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest - 255, // 448: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest - 257, // 449: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest - 260, // 450: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest - 262, // 451: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest - 264, // 452: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest - 55, // 453: openshell.v1.SandboxUpdates.UpdateSandbox:input_type -> openshell.v1.UpdateSandboxRequest - 27, // 454: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse - 29, // 455: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse - 31, // 456: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse - 77, // 457: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse - 66, // 458: openshell.v1.OpenShell.BeginRootfsTarStaging:output_type -> openshell.v1.BeginRootfsTarStagingResponse - 77, // 459: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse - 78, // 460: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse - 62, // 461: openshell.v1.OpenShell.CreateSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse - 62, // 462: openshell.v1.OpenShell.GetSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse - 63, // 463: openshell.v1.OpenShell.ListSandboxTemplates:output_type -> openshell.v1.ListSandboxTemplatesResponse - 64, // 464: openshell.v1.OpenShell.DeleteSandboxTemplate:output_type -> openshell.v1.DeleteSandboxTemplateResponse - 79, // 465: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse - 80, // 466: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse - 81, // 467: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse - 90, // 468: openshell.v1.OpenShell.GetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse - 93, // 469: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse - 77, // 470: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse - 77, // 471: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse - 77, // 472: openshell.v1.OpenShell.SuspendSandbox:output_type -> openshell.v1.SandboxResponse - 77, // 473: openshell.v1.OpenShell.ResumeSandbox:output_type -> openshell.v1.SandboxResponse - 95, // 474: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse - 103, // 475: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse - 103, // 476: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse - 99, // 477: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse - 101, // 478: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse - 105, // 479: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse - 110, // 480: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent - 112, // 481: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame - 110, // 482: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent - 128, // 483: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse - 128, // 484: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse - 124, // 485: openshell.v1.OpenShell.GetProviderCredentials:output_type -> openshell.v1.GetProviderCredentialsResponse - 129, // 486: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse - 154, // 487: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse - 153, // 488: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse - 156, // 489: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse - 158, // 490: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse - 160, // 491: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse - 128, // 492: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse - 144, // 493: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse - 146, // 494: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse - 148, // 495: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse - 150, // 496: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse - 161, // 497: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse - 163, // 498: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse - 321, // 499: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse - 322, // 500: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse - 181, // 501: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse - 183, // 502: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse - 185, // 503: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse - 187, // 504: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse - 271, // 505: openshell.v1.OpenShell.ReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse - 92, // 506: openshell.v1.OpenShell.ReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse - 190, // 507: openshell.v1.OpenShell.ReportSandboxConfiguration:output_type -> openshell.v1.ReportSandboxConfigurationResponse - 167, // 508: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse - 171, // 509: openshell.v1.OpenShell.ExchangeProviderSubjectToken:output_type -> openshell.v1.ExchangeProviderSubjectTokenResponse - 195, // 510: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse - 194, // 511: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse - 197, // 512: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage - 209, // 513: openshell.v1.OpenShell.ReportMainProcessExit:output_type -> openshell.v1.ReportMainProcessExitResponse - 211, // 514: openshell.v1.OpenShell.FinalizeMainProcessExit:output_type -> openshell.v1.FinalizeMainProcessExitResponse - 216, // 515: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame - 218, // 516: openshell.v1.OpenShell.PeerRelay:output_type -> openshell.v1.PeerRelayFrame - 92, // 517: openshell.v1.OpenShell.PeerReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse - 271, // 518: openshell.v1.OpenShell.PeerReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse - 90, // 519: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse - 222, // 520: openshell.v1.OpenShell.PeerNotifyConfigUpdate:output_type -> openshell.v1.PeerNotifyConfigUpdateResponse - 117, // 521: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent - 232, // 522: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse - 234, // 523: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse - 236, // 524: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse - 238, // 525: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse - 241, // 526: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse - 243, // 527: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse - 245, // 528: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse - 247, // 529: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse - 250, // 530: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse - 23, // 531: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse - 25, // 532: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse - 252, // 533: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse - 254, // 534: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse - 256, // 535: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse - 258, // 536: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse - 261, // 537: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse - 263, // 538: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse - 265, // 539: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse - 77, // 540: openshell.v1.SandboxUpdates.UpdateSandbox:output_type -> openshell.v1.SandboxResponse - 454, // [454:541] is the sub-list for method output_type - 367, // [367:454] is the sub-list for method input_type - 367, // [367:367] is the sub-list for extension type_name - 367, // [367:367] is the sub-list for extension extendee - 0, // [0:367] is the sub-list for field type_name -} - -func init() { file_openshell_proto_init() } -func file_openshell_proto_init() { - if File_openshell_proto != nil { - return - } - file_openshell_proto_msgTypes[20].OneofWrappers = []any{} - file_openshell_proto_msgTypes[21].OneofWrappers = []any{} - file_openshell_proto_msgTypes[29].OneofWrappers = []any{} - file_openshell_proto_msgTypes[33].OneofWrappers = []any{} - file_openshell_proto_msgTypes[61].OneofWrappers = []any{ - (*ConfigSnapshotRevision_SandboxConfig)(nil), - (*ConfigSnapshotRevision_ProviderEnvironment)(nil), - (*ConfigSnapshotRevision_ProviderTarget)(nil), - } - file_openshell_proto_msgTypes[88].OneofWrappers = []any{ - (*ExecSandboxEvent_Stdout)(nil), - (*ExecSandboxEvent_Stderr)(nil), - (*ExecSandboxEvent_Exit)(nil), - } - file_openshell_proto_msgTypes[89].OneofWrappers = []any{ - (*TcpForwardInit_Ssh)(nil), - (*TcpForwardInit_Tcp)(nil), - } - file_openshell_proto_msgTypes[90].OneofWrappers = []any{ - (*TcpForwardFrame_Init)(nil), - (*TcpForwardFrame_Data)(nil), - } - file_openshell_proto_msgTypes[91].OneofWrappers = []any{ - (*ExecSandboxInput_Start)(nil), - (*ExecSandboxInput_Stdin)(nil), - (*ExecSandboxInput_Resize)(nil), - } - file_openshell_proto_msgTypes[95].OneofWrappers = []any{ - (*SandboxStreamEvent_Sandbox)(nil), - (*SandboxStreamEvent_Log)(nil), - (*SandboxStreamEvent_Event)(nil), - (*SandboxStreamEvent_Warning)(nil), - (*SandboxStreamEvent_DraftPolicyUpdate)(nil), - } - file_openshell_proto_msgTypes[151].OneofWrappers = []any{ - (*PolicyMergeOperation_AddRule)(nil), - (*PolicyMergeOperation_RemoveEndpoint)(nil), - (*PolicyMergeOperation_RemoveRule)(nil), - (*PolicyMergeOperation_AddDenyRules)(nil), - (*PolicyMergeOperation_AddAllowRules)(nil), - (*PolicyMergeOperation_RemoveBinary)(nil), - } - file_openshell_proto_msgTypes[155].OneofWrappers = []any{} - file_openshell_proto_msgTypes[174].OneofWrappers = []any{ - (*SupervisorMessage_Hello)(nil), - (*SupervisorMessage_Heartbeat)(nil), - (*SupervisorMessage_RelayOpenResult)(nil), - (*SupervisorMessage_RelayClose)(nil), - (*SupervisorMessage_ConfigUpdateResult)(nil), - } - file_openshell_proto_msgTypes[175].OneofWrappers = []any{ - (*GatewayMessage_SessionAccepted)(nil), - (*GatewayMessage_SessionRejected)(nil), - (*GatewayMessage_Heartbeat)(nil), - (*GatewayMessage_RelayOpen)(nil), - (*GatewayMessage_RelayClose)(nil), - (*GatewayMessage_SessionRedirect)(nil), - (*GatewayMessage_ConfigUpdate)(nil), - } - file_openshell_proto_msgTypes[190].OneofWrappers = []any{ - (*RelayOpen_Ssh)(nil), - (*RelayOpen_Tcp)(nil), - } - file_openshell_proto_msgTypes[194].OneofWrappers = []any{ - (*RelayFrame_Init)(nil), - (*RelayFrame_Data)(nil), - } - file_openshell_proto_msgTypes[196].OneofWrappers = []any{ - (*PeerRelayFrame_Init)(nil), - (*PeerRelayFrame_Data)(nil), - } - file_openshell_proto_msgTypes[199].OneofWrappers = []any{ - (*PeerNotifyConfigUpdateRequest_Sandbox)(nil), - (*PeerNotifyConfigUpdateRequest_Workspace)(nil), - (*PeerNotifyConfigUpdateRequest_AllConnected)(nil), - (*PeerNotifyConfigUpdateRequest_Provider)(nil), - } - file_openshell_proto_msgTypes[253].OneofWrappers = []any{} - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_openshell_proto_rawDesc), len(file_openshell_proto_rawDesc)), - NumEnums: 22, - NumMessages: 281, - NumExtensions: 0, - NumServices: 2, - }, - GoTypes: file_openshell_proto_goTypes, - DependencyIndexes: file_openshell_proto_depIdxs, - EnumInfos: file_openshell_proto_enumTypes, - MessageInfos: file_openshell_proto_msgTypes, - }.Build() - File_openshell_proto = out.File - file_openshell_proto_goTypes = nil - file_openshell_proto_depIdxs = nil -} diff --git a/sdk/go/proto/openshellv1/openshell_grpc.pb.go b/sdk/go/proto/openshellv1/openshell_grpc.pb.go deleted file mode 100644 index fbba50b84c..0000000000 --- a/sdk/go/proto/openshellv1/openshell_grpc.pb.go +++ /dev/null @@ -1,3753 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.6.2 -// - protoc (unknown) -// source: openshell.proto - -package openshellv1 - -import ( - context "context" - sandboxv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/sandboxv1" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - OpenShell_Health_FullMethodName = "/openshell.v1.OpenShell/Health" - OpenShell_GetCurrentUser_FullMethodName = "/openshell.v1.OpenShell/GetCurrentUser" - OpenShell_GetGatewayInfo_FullMethodName = "/openshell.v1.OpenShell/GetGatewayInfo" - OpenShell_CreateSandbox_FullMethodName = "/openshell.v1.OpenShell/CreateSandbox" - OpenShell_BeginRootfsTarStaging_FullMethodName = "/openshell.v1.OpenShell/BeginRootfsTarStaging" - OpenShell_GetSandbox_FullMethodName = "/openshell.v1.OpenShell/GetSandbox" - OpenShell_ListSandboxes_FullMethodName = "/openshell.v1.OpenShell/ListSandboxes" - OpenShell_CreateSandboxTemplate_FullMethodName = "/openshell.v1.OpenShell/CreateSandboxTemplate" - OpenShell_GetSandboxTemplate_FullMethodName = "/openshell.v1.OpenShell/GetSandboxTemplate" - OpenShell_ListSandboxTemplates_FullMethodName = "/openshell.v1.OpenShell/ListSandboxTemplates" - OpenShell_DeleteSandboxTemplate_FullMethodName = "/openshell.v1.OpenShell/DeleteSandboxTemplate" - OpenShell_ListSandboxProviders_FullMethodName = "/openshell.v1.OpenShell/ListSandboxProviders" - OpenShell_AttachSandboxProvider_FullMethodName = "/openshell.v1.OpenShell/AttachSandboxProvider" - OpenShell_DetachSandboxProvider_FullMethodName = "/openshell.v1.OpenShell/DetachSandboxProvider" - OpenShell_GetSandboxProviderStatus_FullMethodName = "/openshell.v1.OpenShell/GetSandboxProviderStatus" - OpenShell_DeleteSandbox_FullMethodName = "/openshell.v1.OpenShell/DeleteSandbox" - OpenShell_StopSandbox_FullMethodName = "/openshell.v1.OpenShell/StopSandbox" - OpenShell_StartSandbox_FullMethodName = "/openshell.v1.OpenShell/StartSandbox" - OpenShell_SuspendSandbox_FullMethodName = "/openshell.v1.OpenShell/SuspendSandbox" - OpenShell_ResumeSandbox_FullMethodName = "/openshell.v1.OpenShell/ResumeSandbox" - OpenShell_CreateSshSession_FullMethodName = "/openshell.v1.OpenShell/CreateSshSession" - OpenShell_ExposeService_FullMethodName = "/openshell.v1.OpenShell/ExposeService" - OpenShell_GetService_FullMethodName = "/openshell.v1.OpenShell/GetService" - OpenShell_ListServices_FullMethodName = "/openshell.v1.OpenShell/ListServices" - OpenShell_DeleteService_FullMethodName = "/openshell.v1.OpenShell/DeleteService" - OpenShell_RevokeSshSession_FullMethodName = "/openshell.v1.OpenShell/RevokeSshSession" - OpenShell_ExecSandbox_FullMethodName = "/openshell.v1.OpenShell/ExecSandbox" - OpenShell_ForwardTcp_FullMethodName = "/openshell.v1.OpenShell/ForwardTcp" - OpenShell_ExecSandboxInteractive_FullMethodName = "/openshell.v1.OpenShell/ExecSandboxInteractive" - OpenShell_CreateProvider_FullMethodName = "/openshell.v1.OpenShell/CreateProvider" - OpenShell_GetProvider_FullMethodName = "/openshell.v1.OpenShell/GetProvider" - OpenShell_GetProviderCredentials_FullMethodName = "/openshell.v1.OpenShell/GetProviderCredentials" - OpenShell_ListProviders_FullMethodName = "/openshell.v1.OpenShell/ListProviders" - OpenShell_ListProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/ListProviderProfiles" - OpenShell_GetProviderProfile_FullMethodName = "/openshell.v1.OpenShell/GetProviderProfile" - OpenShell_ImportProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/ImportProviderProfiles" - OpenShell_UpdateProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/UpdateProviderProfiles" - OpenShell_LintProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/LintProviderProfiles" - OpenShell_UpdateProvider_FullMethodName = "/openshell.v1.OpenShell/UpdateProvider" - OpenShell_GetProviderRefreshStatus_FullMethodName = "/openshell.v1.OpenShell/GetProviderRefreshStatus" - OpenShell_ConfigureProviderRefresh_FullMethodName = "/openshell.v1.OpenShell/ConfigureProviderRefresh" - OpenShell_RotateProviderCredential_FullMethodName = "/openshell.v1.OpenShell/RotateProviderCredential" - OpenShell_DeleteProviderRefresh_FullMethodName = "/openshell.v1.OpenShell/DeleteProviderRefresh" - OpenShell_DeleteProvider_FullMethodName = "/openshell.v1.OpenShell/DeleteProvider" - OpenShell_DeleteProviderProfile_FullMethodName = "/openshell.v1.OpenShell/DeleteProviderProfile" - OpenShell_GetSandboxConfig_FullMethodName = "/openshell.v1.OpenShell/GetSandboxConfig" - OpenShell_GetGatewayConfig_FullMethodName = "/openshell.v1.OpenShell/GetGatewayConfig" - OpenShell_UpdateConfig_FullMethodName = "/openshell.v1.OpenShell/UpdateConfig" - OpenShell_GetSandboxPolicyStatus_FullMethodName = "/openshell.v1.OpenShell/GetSandboxPolicyStatus" - OpenShell_ListSandboxPolicies_FullMethodName = "/openshell.v1.OpenShell/ListSandboxPolicies" - OpenShell_ReportPolicyStatus_FullMethodName = "/openshell.v1.OpenShell/ReportPolicyStatus" - OpenShell_ReportEndpointStatus_FullMethodName = "/openshell.v1.OpenShell/ReportEndpointStatus" - OpenShell_ReportProviderReadiness_FullMethodName = "/openshell.v1.OpenShell/ReportProviderReadiness" - OpenShell_ReportSandboxConfiguration_FullMethodName = "/openshell.v1.OpenShell/ReportSandboxConfiguration" - OpenShell_GetSandboxProviderEnvironment_FullMethodName = "/openshell.v1.OpenShell/GetSandboxProviderEnvironment" - OpenShell_ExchangeProviderSubjectToken_FullMethodName = "/openshell.v1.OpenShell/ExchangeProviderSubjectToken" - OpenShell_GetSandboxLogs_FullMethodName = "/openshell.v1.OpenShell/GetSandboxLogs" - OpenShell_PushSandboxLogs_FullMethodName = "/openshell.v1.OpenShell/PushSandboxLogs" - OpenShell_ConnectSupervisor_FullMethodName = "/openshell.v1.OpenShell/ConnectSupervisor" - OpenShell_ReportMainProcessExit_FullMethodName = "/openshell.v1.OpenShell/ReportMainProcessExit" - OpenShell_FinalizeMainProcessExit_FullMethodName = "/openshell.v1.OpenShell/FinalizeMainProcessExit" - OpenShell_RelayStream_FullMethodName = "/openshell.v1.OpenShell/RelayStream" - OpenShell_PeerRelay_FullMethodName = "/openshell.v1.OpenShell/PeerRelay" - OpenShell_PeerReportProviderReadiness_FullMethodName = "/openshell.v1.OpenShell/PeerReportProviderReadiness" - OpenShell_PeerReportEndpointStatus_FullMethodName = "/openshell.v1.OpenShell/PeerReportEndpointStatus" - OpenShell_PeerGetSandboxProviderStatus_FullMethodName = "/openshell.v1.OpenShell/PeerGetSandboxProviderStatus" - OpenShell_PeerNotifyConfigUpdate_FullMethodName = "/openshell.v1.OpenShell/PeerNotifyConfigUpdate" - OpenShell_WatchSandbox_FullMethodName = "/openshell.v1.OpenShell/WatchSandbox" - OpenShell_SubmitPolicyAnalysis_FullMethodName = "/openshell.v1.OpenShell/SubmitPolicyAnalysis" - OpenShell_GetDraftPolicy_FullMethodName = "/openshell.v1.OpenShell/GetDraftPolicy" - OpenShell_ApproveDraftChunk_FullMethodName = "/openshell.v1.OpenShell/ApproveDraftChunk" - OpenShell_RejectDraftChunk_FullMethodName = "/openshell.v1.OpenShell/RejectDraftChunk" - OpenShell_ApproveAllDraftChunks_FullMethodName = "/openshell.v1.OpenShell/ApproveAllDraftChunks" - OpenShell_EditDraftChunk_FullMethodName = "/openshell.v1.OpenShell/EditDraftChunk" - OpenShell_UndoDraftChunk_FullMethodName = "/openshell.v1.OpenShell/UndoDraftChunk" - OpenShell_ClearDraftChunks_FullMethodName = "/openshell.v1.OpenShell/ClearDraftChunks" - OpenShell_GetDraftHistory_FullMethodName = "/openshell.v1.OpenShell/GetDraftHistory" - OpenShell_IssueSandboxToken_FullMethodName = "/openshell.v1.OpenShell/IssueSandboxToken" - OpenShell_RefreshSandboxToken_FullMethodName = "/openshell.v1.OpenShell/RefreshSandboxToken" - OpenShell_CreateWorkspace_FullMethodName = "/openshell.v1.OpenShell/CreateWorkspace" - OpenShell_GetWorkspace_FullMethodName = "/openshell.v1.OpenShell/GetWorkspace" - OpenShell_ListWorkspaces_FullMethodName = "/openshell.v1.OpenShell/ListWorkspaces" - OpenShell_DeleteWorkspace_FullMethodName = "/openshell.v1.OpenShell/DeleteWorkspace" - OpenShell_AddWorkspaceMember_FullMethodName = "/openshell.v1.OpenShell/AddWorkspaceMember" - OpenShell_RemoveWorkspaceMember_FullMethodName = "/openshell.v1.OpenShell/RemoveWorkspaceMember" - OpenShell_ListWorkspaceMembers_FullMethodName = "/openshell.v1.OpenShell/ListWorkspaceMembers" -) - -// OpenShellClient is the client API for OpenShell service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -// -// OpenShell service provides sandbox, provider, and runtime management capabilities. -// -// Conventions: -// - This file owns the public API resource model exposed to OpenShell clients. -// - `Sandbox`, `SandboxSpec`, `SandboxStatus`, and `SandboxPhase` are gateway-owned -// public types. Internal compute drivers must not import or return them directly. -// - The gateway translates internal compute-driver observations into these public -// resource messages before persisting or returning them to clients. -type OpenShellClient interface { - // Check the health of the service. - Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error) - // Return the authenticated caller identity established by the gateway. - GetCurrentUser(ctx context.Context, in *GetCurrentUserRequest, opts ...grpc.CallOption) (*GetCurrentUserResponse, error) - // Fetch elevated live gateway runtime metadata. - GetGatewayInfo(ctx context.Context, in *GetGatewayInfoRequest, opts ...grpc.CallOption) (*GetGatewayInfoResponse, error) - // Create a new sandbox. - CreateSandbox(ctx context.Context, in *CreateSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) - // Allocate a gateway-owned staging slot for a local rootfs tar archive. - // - // The gateway creates a request-scoped directory inside the compute driver's - // staging root and returns an opaque single-use token plus the absolute path - // the client must write the archive to. The token is then passed as - // `template.driver_config..rootfs_tar_staging_token` on - // CreateSandbox; callers never name a filesystem path themselves. Only a - // client sharing the gateway's filesystem can complete the upload. - BeginRootfsTarStaging(ctx context.Context, in *BeginRootfsTarStagingRequest, opts ...grpc.CallOption) (*BeginRootfsTarStagingResponse, error) - // Fetch a sandbox by name. - GetSandbox(ctx context.Context, in *GetSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) - // List sandboxes. - ListSandboxes(ctx context.Context, in *ListSandboxesRequest, opts ...grpc.CallOption) (*ListSandboxesResponse, error) - // Create a reusable sandbox workload template. - CreateSandboxTemplate(ctx context.Context, in *CreateSandboxTemplateRequest, opts ...grpc.CallOption) (*SandboxTemplateResponse, error) - // Fetch a reusable sandbox workload template by name. - GetSandboxTemplate(ctx context.Context, in *GetSandboxTemplateRequest, opts ...grpc.CallOption) (*SandboxTemplateResponse, error) - // List reusable sandbox workload templates. - ListSandboxTemplates(ctx context.Context, in *ListSandboxTemplatesRequest, opts ...grpc.CallOption) (*ListSandboxTemplatesResponse, error) - // Delete a reusable sandbox workload template by name. - DeleteSandboxTemplate(ctx context.Context, in *DeleteSandboxTemplateRequest, opts ...grpc.CallOption) (*DeleteSandboxTemplateResponse, error) - // List provider records attached to a sandbox. - ListSandboxProviders(ctx context.Context, in *ListSandboxProvidersRequest, opts ...grpc.CallOption) (*ListSandboxProvidersResponse, error) - // Attach a provider record to an existing sandbox. - AttachSandboxProvider(ctx context.Context, in *AttachSandboxProviderRequest, opts ...grpc.CallOption) (*AttachSandboxProviderResponse, error) - // Detach a provider record from an existing sandbox. - DetachSandboxProvider(ctx context.Context, in *DetachSandboxProviderRequest, opts ...grpc.CallOption) (*DetachSandboxProviderResponse, error) - // Inspect the installed authority for one sandbox provider mutation. - GetSandboxProviderStatus(ctx context.Context, in *GetSandboxProviderStatusRequest, opts ...grpc.CallOption) (*GetSandboxProviderStatusResponse, error) - // Delete a sandbox by name. - DeleteSandbox(ctx context.Context, in *DeleteSandboxRequest, opts ...grpc.CallOption) (*DeleteSandboxResponse, error) - // Stop a sandbox while retaining its persistent state. - StopSandbox(ctx context.Context, in *StopSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) - // Start a previously stopped sandbox. - StartSandbox(ctx context.Context, in *StartSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) - // Freeze a ready sandbox and retain its memory state for a later resume. - // Requires a compute driver and isolation backend that support state - // retention. - SuspendSandbox(ctx context.Context, in *SuspendSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) - // Restore a suspended sandbox in a new runtime generation. - ResumeSandbox(ctx context.Context, in *ResumeSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) - // Create a short-lived SSH session for a sandbox. - CreateSshSession(ctx context.Context, in *CreateSshSessionRequest, opts ...grpc.CallOption) (*CreateSshSessionResponse, error) - // Create or update a sandbox HTTP service endpoint for local routing. - ExposeService(ctx context.Context, in *ExposeServiceRequest, opts ...grpc.CallOption) (*ServiceEndpointResponse, error) - // Fetch one sandbox HTTP service endpoint. - GetService(ctx context.Context, in *GetServiceRequest, opts ...grpc.CallOption) (*ServiceEndpointResponse, error) - // List sandbox HTTP service endpoints. - ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error) - // Delete one sandbox HTTP service endpoint. - DeleteService(ctx context.Context, in *DeleteServiceRequest, opts ...grpc.CallOption) (*DeleteServiceResponse, error) - // Revoke a previously issued SSH session. - RevokeSshSession(ctx context.Context, in *RevokeSshSessionRequest, opts ...grpc.CallOption) (*RevokeSshSessionResponse, error) - // Execute a command in a ready sandbox and stream output. - ExecSandbox(ctx context.Context, in *ExecSandboxRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ExecSandboxEvent], error) - // Forward one CLI-side TCP connection to a loopback TCP target in a sandbox. - ForwardTcp(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[TcpForwardFrame, TcpForwardFrame], error) - // Execute an interactive command with bidirectional stdin/stdout streaming. - // The first client message MUST carry an ExecSandboxInput with the start - // variant. Subsequent messages carry stdin bytes or window resize events. - ExecSandboxInteractive(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ExecSandboxInput, ExecSandboxEvent], error) - // Create a provider. - CreateProvider(ctx context.Context, in *CreateProviderRequest, opts ...grpc.CallOption) (*ProviderResponse, error) - // Fetch a provider by name. - GetProvider(ctx context.Context, in *GetProviderRequest, opts ...grpc.CallOption) (*ProviderResponse, error) - // Export selected usable runtime credentials. Only directly authenticated - // mTLS operators may call this method; refresh material is never returned. - GetProviderCredentials(ctx context.Context, in *GetProviderCredentialsRequest, opts ...grpc.CallOption) (*GetProviderCredentialsResponse, error) - // List providers. - ListProviders(ctx context.Context, in *ListProvidersRequest, opts ...grpc.CallOption) (*ListProvidersResponse, error) - // List available provider type profiles. - ListProviderProfiles(ctx context.Context, in *ListProviderProfilesRequest, opts ...grpc.CallOption) (*ListProviderProfilesResponse, error) - // Fetch one provider type profile by id. - GetProviderProfile(ctx context.Context, in *GetProviderProfileRequest, opts ...grpc.CallOption) (*ProviderProfileResponse, error) - // Import custom provider type profiles. - ImportProviderProfiles(ctx context.Context, in *ImportProviderProfilesRequest, opts ...grpc.CallOption) (*ImportProviderProfilesResponse, error) - // Update an existing custom provider type profile. - UpdateProviderProfiles(ctx context.Context, in *UpdateProviderProfilesRequest, opts ...grpc.CallOption) (*UpdateProviderProfilesResponse, error) - // Validate provider type profiles without registering them. - LintProviderProfiles(ctx context.Context, in *LintProviderProfilesRequest, opts ...grpc.CallOption) (*LintProviderProfilesResponse, error) - // Update an existing provider by name. - UpdateProvider(ctx context.Context, in *UpdateProviderRequest, opts ...grpc.CallOption) (*ProviderResponse, error) - // Fetch refresh status for one provider or provider credential. - GetProviderRefreshStatus(ctx context.Context, in *GetProviderRefreshStatusRequest, opts ...grpc.CallOption) (*GetProviderRefreshStatusResponse, error) - // Configure gateway-owned refresh material for one provider credential. - ConfigureProviderRefresh(ctx context.Context, in *ConfigureProviderRefreshRequest, opts ...grpc.CallOption) (*ConfigureProviderRefreshResponse, error) - // Record a gateway-owned refresh request for one provider credential. - RotateProviderCredential(ctx context.Context, in *RotateProviderCredentialRequest, opts ...grpc.CallOption) (*RotateProviderCredentialResponse, error) - // Delete gateway-owned refresh configuration for one provider credential. - DeleteProviderRefresh(ctx context.Context, in *DeleteProviderRefreshRequest, opts ...grpc.CallOption) (*DeleteProviderRefreshResponse, error) - // Delete a provider by name. - DeleteProvider(ctx context.Context, in *DeleteProviderRequest, opts ...grpc.CallOption) (*DeleteProviderResponse, error) - // Delete a custom provider type profile by id. - DeleteProviderProfile(ctx context.Context, in *DeleteProviderProfileRequest, opts ...grpc.CallOption) (*DeleteProviderProfileResponse, error) - // Get sandbox settings by id (called by users, and by supervisors at startup - // and in their poll loop). A supervisor whose session pushes configuration - // receives later updates over ConnectSupervisor and stops polling. - GetSandboxConfig(ctx context.Context, in *sandboxv1.GetSandboxConfigRequest, opts ...grpc.CallOption) (*sandboxv1.GetSandboxConfigResponse, error) - // Get gateway-global settings (read-only runtime configuration; any - // authenticated user may read these without requiring Platform Admin). - // - // Scope-only (no role): scopes are granted by the IdP at token issuance, - // orthogonal to workspace membership. Deployments that enable scope - // enforcement configure the IdP to grant config:read (or openshell:all) - // to all sandbox users, so this does not block least-privilege flows. - GetGatewayConfig(ctx context.Context, in *sandboxv1.GetGatewayConfigRequest, opts ...grpc.CallOption) (*sandboxv1.GetGatewayConfigResponse, error) - // Update settings or policy at sandbox or global scope. - UpdateConfig(ctx context.Context, in *UpdateConfigRequest, opts ...grpc.CallOption) (*UpdateConfigResponse, error) - // Get the load status of a specific policy version. - GetSandboxPolicyStatus(ctx context.Context, in *GetSandboxPolicyStatusRequest, opts ...grpc.CallOption) (*GetSandboxPolicyStatusResponse, error) - // List policy history for a sandbox. - ListSandboxPolicies(ctx context.Context, in *ListSandboxPoliciesRequest, opts ...grpc.CallOption) (*ListSandboxPoliciesResponse, error) - // Report policy load result (called by sandbox after reload attempt). - ReportPolicyStatus(ctx context.Context, in *ReportPolicyStatusRequest, opts ...grpc.CallOption) (*ReportPolicyStatusResponse, error) - // Replace the gateway's observed tool server endpoint status for one sandbox. - ReportEndpointStatus(ctx context.Context, in *ReportEndpointStatusRequest, opts ...grpc.CallOption) (*ReportEndpointStatusResponse, error) - // Report installed provider state for the current ConnectSupervisor session. - // Replacing or losing that session invalidates its observations. - ReportProviderReadiness(ctx context.Context, in *ReportProviderReadinessRequest, opts ...grpc.CallOption) (*ReportProviderReadinessResponse, error) - // Register startup and acknowledge an exact validated runtime configuration. - ReportSandboxConfiguration(ctx context.Context, in *ReportSandboxConfigurationRequest, opts ...grpc.CallOption) (*ReportSandboxConfigurationResponse, error) - // Get provider environment for a sandbox (called by the sandbox supervisor at - // startup and by its poll loop). Polling projection of the provider - // environment delivered over ConnectSupervisor; removed with the polling RPCs. - GetSandboxProviderEnvironment(ctx context.Context, in *GetSandboxProviderEnvironmentRequest, opts ...grpc.CallOption) (*GetSandboxProviderEnvironmentResponse, error) - // Exchange a stored provider subject token for an intermediate token scoped - // to the calling supervisor's SPIFFE identity. - ExchangeProviderSubjectToken(ctx context.Context, in *ExchangeProviderSubjectTokenRequest, opts ...grpc.CallOption) (*ExchangeProviderSubjectTokenResponse, error) - // Fetch recent sandbox logs (one-shot). - GetSandboxLogs(ctx context.Context, in *GetSandboxLogsRequest, opts ...grpc.CallOption) (*GetSandboxLogsResponse, error) - // Push sandbox supervisor logs to the server (client-streaming). - PushSandboxLogs(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[PushSandboxLogsRequest, PushSandboxLogsResponse], error) - // Persistent supervisor-to-gateway session (bidirectional streaming). - // - // The supervisor opens this stream at startup and keeps it alive for the - // sandbox lifetime. The gateway uses it to coordinate relay channels for - // SSH connect, ExecSandbox, targetable sandbox services, and configuration - // delivery. Supervisors advertise optional stream features in - // SupervisorHello, and the gateway sends only payloads the supervisor - // advertises. Raw service bytes flow over RelayStream calls (separate HTTP/2 - // streams on the same connection), not over this stream. - ConnectSupervisor(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SupervisorMessage, GatewayMessage], error) - // Persist the canonical main process result before the supervisor exits. - ReportMainProcessExit(ctx context.Context, in *ReportMainProcessExitRequest, opts ...grpc.CallOption) (*ReportMainProcessExitResponse, error) - // Confirm that foreground terminal delivery completed naturally. - FinalizeMainProcessExit(ctx context.Context, in *FinalizeMainProcessExitRequest, opts ...grpc.CallOption) (*FinalizeMainProcessExitResponse, error) - // Raw byte relay between supervisor and gateway. - // - // The supervisor initiates this call after receiving a RelayOpen message - // on its ConnectSupervisor stream. The first RelayFrame carries a - // RelayInit with the channel_id to associate the new HTTP/2 stream with - // the pending relay slot on the gateway. Subsequent frames carry raw bytes in either - // direction between the gateway-side waiter (ForwardTcp / exec handler) - // and the supervisor-side target bridge. - // - // This rides the same TCP+TLS+HTTP/2 connection as ConnectSupervisor — - // no new TLS handshake, no reverse HTTP CONNECT. - RelayStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[RelayFrame, RelayFrame], error) - // Internal gateway-to-gateway relay forwarding. - // - // A gateway replica that receives a user request for a sandbox whose - // supervisor session is owned by a different replica opens this stream to the - // owner. The first frame carries PeerRelayInit; subsequent frames carry raw - // bytes in either direction. This RPC is authenticated as a gateway peer, not - // as a user or sandbox supervisor. - PeerRelay(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[PeerRelayFrame, PeerRelayFrame], error) - // Internal gateway-to-owner forwarding for supervisor-bound state. - PeerReportProviderReadiness(ctx context.Context, in *ReportProviderReadinessRequest, opts ...grpc.CallOption) (*ReportProviderReadinessResponse, error) - PeerReportEndpointStatus(ctx context.Context, in *ReportEndpointStatusRequest, opts ...grpc.CallOption) (*ReportEndpointStatusResponse, error) - PeerGetSandboxProviderStatus(ctx context.Context, in *GetSandboxProviderStatusRequest, opts ...grpc.CallOption) (*GetSandboxProviderStatusResponse, error) - // Best-effort notification to the replica that owns a supervisor session. - // The receiver rebuilds snapshots from authoritative state. - PeerNotifyConfigUpdate(ctx context.Context, in *PeerNotifyConfigUpdateRequest, opts ...grpc.CallOption) (*PeerNotifyConfigUpdateResponse, error) - // Watch a sandbox and stream updates. - // - // This stream can include: - // - Sandbox status snapshots (phase/status) - // - OpenShell server process logs correlated by sandbox_id - // - Platform events correlated to the sandbox - WatchSandbox(ctx context.Context, in *WatchSandboxRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SandboxStreamEvent], error) - // Submit denial analysis results from sandbox (summaries + proposed chunks). - SubmitPolicyAnalysis(ctx context.Context, in *SubmitPolicyAnalysisRequest, opts ...grpc.CallOption) (*SubmitPolicyAnalysisResponse, error) - // Get draft policy recommendations for a sandbox. - GetDraftPolicy(ctx context.Context, in *GetDraftPolicyRequest, opts ...grpc.CallOption) (*GetDraftPolicyResponse, error) - // Approve a single draft policy chunk (merges into active policy). - ApproveDraftChunk(ctx context.Context, in *ApproveDraftChunkRequest, opts ...grpc.CallOption) (*ApproveDraftChunkResponse, error) - // Reject a single draft policy chunk. - RejectDraftChunk(ctx context.Context, in *RejectDraftChunkRequest, opts ...grpc.CallOption) (*RejectDraftChunkResponse, error) - // Approve all pending draft chunks (skips security-flagged unless forced). - ApproveAllDraftChunks(ctx context.Context, in *ApproveAllDraftChunksRequest, opts ...grpc.CallOption) (*ApproveAllDraftChunksResponse, error) - // Edit a pending draft chunk in-place (e.g. narrow allowed_ips). - EditDraftChunk(ctx context.Context, in *EditDraftChunkRequest, opts ...grpc.CallOption) (*EditDraftChunkResponse, error) - // Reverse an approval (remove merged rule from active policy). - UndoDraftChunk(ctx context.Context, in *UndoDraftChunkRequest, opts ...grpc.CallOption) (*UndoDraftChunkResponse, error) - // Clear all pending draft chunks for a sandbox. - ClearDraftChunks(ctx context.Context, in *ClearDraftChunksRequest, opts ...grpc.CallOption) (*ClearDraftChunksResponse, error) - // Get decision history for a sandbox's draft policy. - GetDraftHistory(ctx context.Context, in *GetDraftHistoryRequest, opts ...grpc.CallOption) (*GetDraftHistoryResponse, error) - // Exchange a sandbox-bootstrap credential (e.g. a Kubernetes projected - // ServiceAccount token) for a gateway-minted JWT bound to the calling - // sandbox's UUID. Used by the Kubernetes driver path; singleplayer - // drivers receive the gateway JWT directly from the create-sandbox flow - // and never call this RPC. - IssueSandboxToken(ctx context.Context, in *IssueSandboxTokenRequest, opts ...grpc.CallOption) (*IssueSandboxTokenResponse, error) - // Renew the calling sandbox's gateway JWT. Older tokens remain valid - // until their own expiry; deployments should keep token TTLs short to - // bound replay exposure. The supervisor calls this from a background - // task at ~80% of the token's lifetime; the new token is cached in - // memory only — the on-disk bootstrap file is intentionally not - // rewritten. - RefreshSandboxToken(ctx context.Context, in *RefreshSandboxTokenRequest, opts ...grpc.CallOption) (*RefreshSandboxTokenResponse, error) - // Create a workspace. - CreateWorkspace(ctx context.Context, in *CreateWorkspaceRequest, opts ...grpc.CallOption) (*CreateWorkspaceResponse, error) - // Fetch a workspace by name. - GetWorkspace(ctx context.Context, in *GetWorkspaceRequest, opts ...grpc.CallOption) (*GetWorkspaceResponse, error) - // List workspaces. - ListWorkspaces(ctx context.Context, in *ListWorkspacesRequest, opts ...grpc.CallOption) (*ListWorkspacesResponse, error) - // Delete a workspace by name. - DeleteWorkspace(ctx context.Context, in *DeleteWorkspaceRequest, opts ...grpc.CallOption) (*DeleteWorkspaceResponse, error) - // Add a member to a workspace. - AddWorkspaceMember(ctx context.Context, in *AddWorkspaceMemberRequest, opts ...grpc.CallOption) (*AddWorkspaceMemberResponse, error) - // Remove a member from a workspace. - RemoveWorkspaceMember(ctx context.Context, in *RemoveWorkspaceMemberRequest, opts ...grpc.CallOption) (*RemoveWorkspaceMemberResponse, error) - // List members of a workspace. - ListWorkspaceMembers(ctx context.Context, in *ListWorkspaceMembersRequest, opts ...grpc.CallOption) (*ListWorkspaceMembersResponse, error) -} - -type openShellClient struct { - cc grpc.ClientConnInterface -} - -func NewOpenShellClient(cc grpc.ClientConnInterface) OpenShellClient { - return &openShellClient{cc} -} - -func (c *openShellClient) Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(HealthResponse) - err := c.cc.Invoke(ctx, OpenShell_Health_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetCurrentUser(ctx context.Context, in *GetCurrentUserRequest, opts ...grpc.CallOption) (*GetCurrentUserResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetCurrentUserResponse) - err := c.cc.Invoke(ctx, OpenShell_GetCurrentUser_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetGatewayInfo(ctx context.Context, in *GetGatewayInfoRequest, opts ...grpc.CallOption) (*GetGatewayInfoResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetGatewayInfoResponse) - err := c.cc.Invoke(ctx, OpenShell_GetGatewayInfo_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) CreateSandbox(ctx context.Context, in *CreateSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SandboxResponse) - err := c.cc.Invoke(ctx, OpenShell_CreateSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) BeginRootfsTarStaging(ctx context.Context, in *BeginRootfsTarStagingRequest, opts ...grpc.CallOption) (*BeginRootfsTarStagingResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(BeginRootfsTarStagingResponse) - err := c.cc.Invoke(ctx, OpenShell_BeginRootfsTarStaging_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetSandbox(ctx context.Context, in *GetSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SandboxResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListSandboxes(ctx context.Context, in *ListSandboxesRequest, opts ...grpc.CallOption) (*ListSandboxesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListSandboxesResponse) - err := c.cc.Invoke(ctx, OpenShell_ListSandboxes_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) CreateSandboxTemplate(ctx context.Context, in *CreateSandboxTemplateRequest, opts ...grpc.CallOption) (*SandboxTemplateResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SandboxTemplateResponse) - err := c.cc.Invoke(ctx, OpenShell_CreateSandboxTemplate_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetSandboxTemplate(ctx context.Context, in *GetSandboxTemplateRequest, opts ...grpc.CallOption) (*SandboxTemplateResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SandboxTemplateResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandboxTemplate_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListSandboxTemplates(ctx context.Context, in *ListSandboxTemplatesRequest, opts ...grpc.CallOption) (*ListSandboxTemplatesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListSandboxTemplatesResponse) - err := c.cc.Invoke(ctx, OpenShell_ListSandboxTemplates_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DeleteSandboxTemplate(ctx context.Context, in *DeleteSandboxTemplateRequest, opts ...grpc.CallOption) (*DeleteSandboxTemplateResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeleteSandboxTemplateResponse) - err := c.cc.Invoke(ctx, OpenShell_DeleteSandboxTemplate_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListSandboxProviders(ctx context.Context, in *ListSandboxProvidersRequest, opts ...grpc.CallOption) (*ListSandboxProvidersResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListSandboxProvidersResponse) - err := c.cc.Invoke(ctx, OpenShell_ListSandboxProviders_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) AttachSandboxProvider(ctx context.Context, in *AttachSandboxProviderRequest, opts ...grpc.CallOption) (*AttachSandboxProviderResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(AttachSandboxProviderResponse) - err := c.cc.Invoke(ctx, OpenShell_AttachSandboxProvider_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DetachSandboxProvider(ctx context.Context, in *DetachSandboxProviderRequest, opts ...grpc.CallOption) (*DetachSandboxProviderResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DetachSandboxProviderResponse) - err := c.cc.Invoke(ctx, OpenShell_DetachSandboxProvider_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetSandboxProviderStatus(ctx context.Context, in *GetSandboxProviderStatusRequest, opts ...grpc.CallOption) (*GetSandboxProviderStatusResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetSandboxProviderStatusResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandboxProviderStatus_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DeleteSandbox(ctx context.Context, in *DeleteSandboxRequest, opts ...grpc.CallOption) (*DeleteSandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeleteSandboxResponse) - err := c.cc.Invoke(ctx, OpenShell_DeleteSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) StopSandbox(ctx context.Context, in *StopSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SandboxResponse) - err := c.cc.Invoke(ctx, OpenShell_StopSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) StartSandbox(ctx context.Context, in *StartSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SandboxResponse) - err := c.cc.Invoke(ctx, OpenShell_StartSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) SuspendSandbox(ctx context.Context, in *SuspendSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SandboxResponse) - err := c.cc.Invoke(ctx, OpenShell_SuspendSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ResumeSandbox(ctx context.Context, in *ResumeSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SandboxResponse) - err := c.cc.Invoke(ctx, OpenShell_ResumeSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) CreateSshSession(ctx context.Context, in *CreateSshSessionRequest, opts ...grpc.CallOption) (*CreateSshSessionResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(CreateSshSessionResponse) - err := c.cc.Invoke(ctx, OpenShell_CreateSshSession_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ExposeService(ctx context.Context, in *ExposeServiceRequest, opts ...grpc.CallOption) (*ServiceEndpointResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ServiceEndpointResponse) - err := c.cc.Invoke(ctx, OpenShell_ExposeService_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetService(ctx context.Context, in *GetServiceRequest, opts ...grpc.CallOption) (*ServiceEndpointResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ServiceEndpointResponse) - err := c.cc.Invoke(ctx, OpenShell_GetService_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListServicesResponse) - err := c.cc.Invoke(ctx, OpenShell_ListServices_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DeleteService(ctx context.Context, in *DeleteServiceRequest, opts ...grpc.CallOption) (*DeleteServiceResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeleteServiceResponse) - err := c.cc.Invoke(ctx, OpenShell_DeleteService_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) RevokeSshSession(ctx context.Context, in *RevokeSshSessionRequest, opts ...grpc.CallOption) (*RevokeSshSessionResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(RevokeSshSessionResponse) - err := c.cc.Invoke(ctx, OpenShell_RevokeSshSession_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ExecSandbox(ctx context.Context, in *ExecSandboxRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ExecSandboxEvent], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[0], OpenShell_ExecSandbox_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[ExecSandboxRequest, ExecSandboxEvent]{ClientStream: stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ExecSandboxClient = grpc.ServerStreamingClient[ExecSandboxEvent] - -func (c *openShellClient) ForwardTcp(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[TcpForwardFrame, TcpForwardFrame], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[1], OpenShell_ForwardTcp_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[TcpForwardFrame, TcpForwardFrame]{ClientStream: stream} - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ForwardTcpClient = grpc.BidiStreamingClient[TcpForwardFrame, TcpForwardFrame] - -func (c *openShellClient) ExecSandboxInteractive(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ExecSandboxInput, ExecSandboxEvent], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[2], OpenShell_ExecSandboxInteractive_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[ExecSandboxInput, ExecSandboxEvent]{ClientStream: stream} - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ExecSandboxInteractiveClient = grpc.BidiStreamingClient[ExecSandboxInput, ExecSandboxEvent] - -func (c *openShellClient) CreateProvider(ctx context.Context, in *CreateProviderRequest, opts ...grpc.CallOption) (*ProviderResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ProviderResponse) - err := c.cc.Invoke(ctx, OpenShell_CreateProvider_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetProvider(ctx context.Context, in *GetProviderRequest, opts ...grpc.CallOption) (*ProviderResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ProviderResponse) - err := c.cc.Invoke(ctx, OpenShell_GetProvider_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetProviderCredentials(ctx context.Context, in *GetProviderCredentialsRequest, opts ...grpc.CallOption) (*GetProviderCredentialsResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetProviderCredentialsResponse) - err := c.cc.Invoke(ctx, OpenShell_GetProviderCredentials_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListProviders(ctx context.Context, in *ListProvidersRequest, opts ...grpc.CallOption) (*ListProvidersResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListProvidersResponse) - err := c.cc.Invoke(ctx, OpenShell_ListProviders_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListProviderProfiles(ctx context.Context, in *ListProviderProfilesRequest, opts ...grpc.CallOption) (*ListProviderProfilesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListProviderProfilesResponse) - err := c.cc.Invoke(ctx, OpenShell_ListProviderProfiles_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetProviderProfile(ctx context.Context, in *GetProviderProfileRequest, opts ...grpc.CallOption) (*ProviderProfileResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ProviderProfileResponse) - err := c.cc.Invoke(ctx, OpenShell_GetProviderProfile_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ImportProviderProfiles(ctx context.Context, in *ImportProviderProfilesRequest, opts ...grpc.CallOption) (*ImportProviderProfilesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ImportProviderProfilesResponse) - err := c.cc.Invoke(ctx, OpenShell_ImportProviderProfiles_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) UpdateProviderProfiles(ctx context.Context, in *UpdateProviderProfilesRequest, opts ...grpc.CallOption) (*UpdateProviderProfilesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(UpdateProviderProfilesResponse) - err := c.cc.Invoke(ctx, OpenShell_UpdateProviderProfiles_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) LintProviderProfiles(ctx context.Context, in *LintProviderProfilesRequest, opts ...grpc.CallOption) (*LintProviderProfilesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(LintProviderProfilesResponse) - err := c.cc.Invoke(ctx, OpenShell_LintProviderProfiles_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) UpdateProvider(ctx context.Context, in *UpdateProviderRequest, opts ...grpc.CallOption) (*ProviderResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ProviderResponse) - err := c.cc.Invoke(ctx, OpenShell_UpdateProvider_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetProviderRefreshStatus(ctx context.Context, in *GetProviderRefreshStatusRequest, opts ...grpc.CallOption) (*GetProviderRefreshStatusResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetProviderRefreshStatusResponse) - err := c.cc.Invoke(ctx, OpenShell_GetProviderRefreshStatus_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ConfigureProviderRefresh(ctx context.Context, in *ConfigureProviderRefreshRequest, opts ...grpc.CallOption) (*ConfigureProviderRefreshResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ConfigureProviderRefreshResponse) - err := c.cc.Invoke(ctx, OpenShell_ConfigureProviderRefresh_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) RotateProviderCredential(ctx context.Context, in *RotateProviderCredentialRequest, opts ...grpc.CallOption) (*RotateProviderCredentialResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(RotateProviderCredentialResponse) - err := c.cc.Invoke(ctx, OpenShell_RotateProviderCredential_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DeleteProviderRefresh(ctx context.Context, in *DeleteProviderRefreshRequest, opts ...grpc.CallOption) (*DeleteProviderRefreshResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeleteProviderRefreshResponse) - err := c.cc.Invoke(ctx, OpenShell_DeleteProviderRefresh_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DeleteProvider(ctx context.Context, in *DeleteProviderRequest, opts ...grpc.CallOption) (*DeleteProviderResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeleteProviderResponse) - err := c.cc.Invoke(ctx, OpenShell_DeleteProvider_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DeleteProviderProfile(ctx context.Context, in *DeleteProviderProfileRequest, opts ...grpc.CallOption) (*DeleteProviderProfileResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeleteProviderProfileResponse) - err := c.cc.Invoke(ctx, OpenShell_DeleteProviderProfile_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetSandboxConfig(ctx context.Context, in *sandboxv1.GetSandboxConfigRequest, opts ...grpc.CallOption) (*sandboxv1.GetSandboxConfigResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(sandboxv1.GetSandboxConfigResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandboxConfig_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetGatewayConfig(ctx context.Context, in *sandboxv1.GetGatewayConfigRequest, opts ...grpc.CallOption) (*sandboxv1.GetGatewayConfigResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(sandboxv1.GetGatewayConfigResponse) - err := c.cc.Invoke(ctx, OpenShell_GetGatewayConfig_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) UpdateConfig(ctx context.Context, in *UpdateConfigRequest, opts ...grpc.CallOption) (*UpdateConfigResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(UpdateConfigResponse) - err := c.cc.Invoke(ctx, OpenShell_UpdateConfig_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetSandboxPolicyStatus(ctx context.Context, in *GetSandboxPolicyStatusRequest, opts ...grpc.CallOption) (*GetSandboxPolicyStatusResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetSandboxPolicyStatusResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandboxPolicyStatus_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListSandboxPolicies(ctx context.Context, in *ListSandboxPoliciesRequest, opts ...grpc.CallOption) (*ListSandboxPoliciesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListSandboxPoliciesResponse) - err := c.cc.Invoke(ctx, OpenShell_ListSandboxPolicies_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ReportPolicyStatus(ctx context.Context, in *ReportPolicyStatusRequest, opts ...grpc.CallOption) (*ReportPolicyStatusResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ReportPolicyStatusResponse) - err := c.cc.Invoke(ctx, OpenShell_ReportPolicyStatus_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ReportEndpointStatus(ctx context.Context, in *ReportEndpointStatusRequest, opts ...grpc.CallOption) (*ReportEndpointStatusResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ReportEndpointStatusResponse) - err := c.cc.Invoke(ctx, OpenShell_ReportEndpointStatus_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ReportProviderReadiness(ctx context.Context, in *ReportProviderReadinessRequest, opts ...grpc.CallOption) (*ReportProviderReadinessResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ReportProviderReadinessResponse) - err := c.cc.Invoke(ctx, OpenShell_ReportProviderReadiness_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ReportSandboxConfiguration(ctx context.Context, in *ReportSandboxConfigurationRequest, opts ...grpc.CallOption) (*ReportSandboxConfigurationResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ReportSandboxConfigurationResponse) - err := c.cc.Invoke(ctx, OpenShell_ReportSandboxConfiguration_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetSandboxProviderEnvironment(ctx context.Context, in *GetSandboxProviderEnvironmentRequest, opts ...grpc.CallOption) (*GetSandboxProviderEnvironmentResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetSandboxProviderEnvironmentResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandboxProviderEnvironment_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ExchangeProviderSubjectToken(ctx context.Context, in *ExchangeProviderSubjectTokenRequest, opts ...grpc.CallOption) (*ExchangeProviderSubjectTokenResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ExchangeProviderSubjectTokenResponse) - err := c.cc.Invoke(ctx, OpenShell_ExchangeProviderSubjectToken_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetSandboxLogs(ctx context.Context, in *GetSandboxLogsRequest, opts ...grpc.CallOption) (*GetSandboxLogsResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetSandboxLogsResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandboxLogs_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) PushSandboxLogs(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[PushSandboxLogsRequest, PushSandboxLogsResponse], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[3], OpenShell_PushSandboxLogs_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[PushSandboxLogsRequest, PushSandboxLogsResponse]{ClientStream: stream} - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_PushSandboxLogsClient = grpc.ClientStreamingClient[PushSandboxLogsRequest, PushSandboxLogsResponse] - -func (c *openShellClient) ConnectSupervisor(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SupervisorMessage, GatewayMessage], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[4], OpenShell_ConnectSupervisor_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[SupervisorMessage, GatewayMessage]{ClientStream: stream} - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ConnectSupervisorClient = grpc.BidiStreamingClient[SupervisorMessage, GatewayMessage] - -func (c *openShellClient) ReportMainProcessExit(ctx context.Context, in *ReportMainProcessExitRequest, opts ...grpc.CallOption) (*ReportMainProcessExitResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ReportMainProcessExitResponse) - err := c.cc.Invoke(ctx, OpenShell_ReportMainProcessExit_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) FinalizeMainProcessExit(ctx context.Context, in *FinalizeMainProcessExitRequest, opts ...grpc.CallOption) (*FinalizeMainProcessExitResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(FinalizeMainProcessExitResponse) - err := c.cc.Invoke(ctx, OpenShell_FinalizeMainProcessExit_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) RelayStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[RelayFrame, RelayFrame], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[5], OpenShell_RelayStream_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[RelayFrame, RelayFrame]{ClientStream: stream} - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_RelayStreamClient = grpc.BidiStreamingClient[RelayFrame, RelayFrame] - -func (c *openShellClient) PeerRelay(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[PeerRelayFrame, PeerRelayFrame], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[6], OpenShell_PeerRelay_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[PeerRelayFrame, PeerRelayFrame]{ClientStream: stream} - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_PeerRelayClient = grpc.BidiStreamingClient[PeerRelayFrame, PeerRelayFrame] - -func (c *openShellClient) PeerReportProviderReadiness(ctx context.Context, in *ReportProviderReadinessRequest, opts ...grpc.CallOption) (*ReportProviderReadinessResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ReportProviderReadinessResponse) - err := c.cc.Invoke(ctx, OpenShell_PeerReportProviderReadiness_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) PeerReportEndpointStatus(ctx context.Context, in *ReportEndpointStatusRequest, opts ...grpc.CallOption) (*ReportEndpointStatusResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ReportEndpointStatusResponse) - err := c.cc.Invoke(ctx, OpenShell_PeerReportEndpointStatus_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) PeerGetSandboxProviderStatus(ctx context.Context, in *GetSandboxProviderStatusRequest, opts ...grpc.CallOption) (*GetSandboxProviderStatusResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetSandboxProviderStatusResponse) - err := c.cc.Invoke(ctx, OpenShell_PeerGetSandboxProviderStatus_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) PeerNotifyConfigUpdate(ctx context.Context, in *PeerNotifyConfigUpdateRequest, opts ...grpc.CallOption) (*PeerNotifyConfigUpdateResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(PeerNotifyConfigUpdateResponse) - err := c.cc.Invoke(ctx, OpenShell_PeerNotifyConfigUpdate_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) WatchSandbox(ctx context.Context, in *WatchSandboxRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SandboxStreamEvent], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[7], OpenShell_WatchSandbox_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[WatchSandboxRequest, SandboxStreamEvent]{ClientStream: stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_WatchSandboxClient = grpc.ServerStreamingClient[SandboxStreamEvent] - -func (c *openShellClient) SubmitPolicyAnalysis(ctx context.Context, in *SubmitPolicyAnalysisRequest, opts ...grpc.CallOption) (*SubmitPolicyAnalysisResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SubmitPolicyAnalysisResponse) - err := c.cc.Invoke(ctx, OpenShell_SubmitPolicyAnalysis_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetDraftPolicy(ctx context.Context, in *GetDraftPolicyRequest, opts ...grpc.CallOption) (*GetDraftPolicyResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetDraftPolicyResponse) - err := c.cc.Invoke(ctx, OpenShell_GetDraftPolicy_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ApproveDraftChunk(ctx context.Context, in *ApproveDraftChunkRequest, opts ...grpc.CallOption) (*ApproveDraftChunkResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ApproveDraftChunkResponse) - err := c.cc.Invoke(ctx, OpenShell_ApproveDraftChunk_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) RejectDraftChunk(ctx context.Context, in *RejectDraftChunkRequest, opts ...grpc.CallOption) (*RejectDraftChunkResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(RejectDraftChunkResponse) - err := c.cc.Invoke(ctx, OpenShell_RejectDraftChunk_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ApproveAllDraftChunks(ctx context.Context, in *ApproveAllDraftChunksRequest, opts ...grpc.CallOption) (*ApproveAllDraftChunksResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ApproveAllDraftChunksResponse) - err := c.cc.Invoke(ctx, OpenShell_ApproveAllDraftChunks_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) EditDraftChunk(ctx context.Context, in *EditDraftChunkRequest, opts ...grpc.CallOption) (*EditDraftChunkResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(EditDraftChunkResponse) - err := c.cc.Invoke(ctx, OpenShell_EditDraftChunk_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) UndoDraftChunk(ctx context.Context, in *UndoDraftChunkRequest, opts ...grpc.CallOption) (*UndoDraftChunkResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(UndoDraftChunkResponse) - err := c.cc.Invoke(ctx, OpenShell_UndoDraftChunk_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ClearDraftChunks(ctx context.Context, in *ClearDraftChunksRequest, opts ...grpc.CallOption) (*ClearDraftChunksResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ClearDraftChunksResponse) - err := c.cc.Invoke(ctx, OpenShell_ClearDraftChunks_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetDraftHistory(ctx context.Context, in *GetDraftHistoryRequest, opts ...grpc.CallOption) (*GetDraftHistoryResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetDraftHistoryResponse) - err := c.cc.Invoke(ctx, OpenShell_GetDraftHistory_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) IssueSandboxToken(ctx context.Context, in *IssueSandboxTokenRequest, opts ...grpc.CallOption) (*IssueSandboxTokenResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(IssueSandboxTokenResponse) - err := c.cc.Invoke(ctx, OpenShell_IssueSandboxToken_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) RefreshSandboxToken(ctx context.Context, in *RefreshSandboxTokenRequest, opts ...grpc.CallOption) (*RefreshSandboxTokenResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(RefreshSandboxTokenResponse) - err := c.cc.Invoke(ctx, OpenShell_RefreshSandboxToken_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) CreateWorkspace(ctx context.Context, in *CreateWorkspaceRequest, opts ...grpc.CallOption) (*CreateWorkspaceResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(CreateWorkspaceResponse) - err := c.cc.Invoke(ctx, OpenShell_CreateWorkspace_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetWorkspace(ctx context.Context, in *GetWorkspaceRequest, opts ...grpc.CallOption) (*GetWorkspaceResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetWorkspaceResponse) - err := c.cc.Invoke(ctx, OpenShell_GetWorkspace_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListWorkspaces(ctx context.Context, in *ListWorkspacesRequest, opts ...grpc.CallOption) (*ListWorkspacesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListWorkspacesResponse) - err := c.cc.Invoke(ctx, OpenShell_ListWorkspaces_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DeleteWorkspace(ctx context.Context, in *DeleteWorkspaceRequest, opts ...grpc.CallOption) (*DeleteWorkspaceResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeleteWorkspaceResponse) - err := c.cc.Invoke(ctx, OpenShell_DeleteWorkspace_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) AddWorkspaceMember(ctx context.Context, in *AddWorkspaceMemberRequest, opts ...grpc.CallOption) (*AddWorkspaceMemberResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(AddWorkspaceMemberResponse) - err := c.cc.Invoke(ctx, OpenShell_AddWorkspaceMember_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) RemoveWorkspaceMember(ctx context.Context, in *RemoveWorkspaceMemberRequest, opts ...grpc.CallOption) (*RemoveWorkspaceMemberResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(RemoveWorkspaceMemberResponse) - err := c.cc.Invoke(ctx, OpenShell_RemoveWorkspaceMember_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListWorkspaceMembers(ctx context.Context, in *ListWorkspaceMembersRequest, opts ...grpc.CallOption) (*ListWorkspaceMembersResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListWorkspaceMembersResponse) - err := c.cc.Invoke(ctx, OpenShell_ListWorkspaceMembers_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// OpenShellServer is the server API for OpenShell service. -// All implementations must embed UnimplementedOpenShellServer -// for forward compatibility. -// -// OpenShell service provides sandbox, provider, and runtime management capabilities. -// -// Conventions: -// - This file owns the public API resource model exposed to OpenShell clients. -// - `Sandbox`, `SandboxSpec`, `SandboxStatus`, and `SandboxPhase` are gateway-owned -// public types. Internal compute drivers must not import or return them directly. -// - The gateway translates internal compute-driver observations into these public -// resource messages before persisting or returning them to clients. -type OpenShellServer interface { - // Check the health of the service. - Health(context.Context, *HealthRequest) (*HealthResponse, error) - // Return the authenticated caller identity established by the gateway. - GetCurrentUser(context.Context, *GetCurrentUserRequest) (*GetCurrentUserResponse, error) - // Fetch elevated live gateway runtime metadata. - GetGatewayInfo(context.Context, *GetGatewayInfoRequest) (*GetGatewayInfoResponse, error) - // Create a new sandbox. - CreateSandbox(context.Context, *CreateSandboxRequest) (*SandboxResponse, error) - // Allocate a gateway-owned staging slot for a local rootfs tar archive. - // - // The gateway creates a request-scoped directory inside the compute driver's - // staging root and returns an opaque single-use token plus the absolute path - // the client must write the archive to. The token is then passed as - // `template.driver_config..rootfs_tar_staging_token` on - // CreateSandbox; callers never name a filesystem path themselves. Only a - // client sharing the gateway's filesystem can complete the upload. - BeginRootfsTarStaging(context.Context, *BeginRootfsTarStagingRequest) (*BeginRootfsTarStagingResponse, error) - // Fetch a sandbox by name. - GetSandbox(context.Context, *GetSandboxRequest) (*SandboxResponse, error) - // List sandboxes. - ListSandboxes(context.Context, *ListSandboxesRequest) (*ListSandboxesResponse, error) - // Create a reusable sandbox workload template. - CreateSandboxTemplate(context.Context, *CreateSandboxTemplateRequest) (*SandboxTemplateResponse, error) - // Fetch a reusable sandbox workload template by name. - GetSandboxTemplate(context.Context, *GetSandboxTemplateRequest) (*SandboxTemplateResponse, error) - // List reusable sandbox workload templates. - ListSandboxTemplates(context.Context, *ListSandboxTemplatesRequest) (*ListSandboxTemplatesResponse, error) - // Delete a reusable sandbox workload template by name. - DeleteSandboxTemplate(context.Context, *DeleteSandboxTemplateRequest) (*DeleteSandboxTemplateResponse, error) - // List provider records attached to a sandbox. - ListSandboxProviders(context.Context, *ListSandboxProvidersRequest) (*ListSandboxProvidersResponse, error) - // Attach a provider record to an existing sandbox. - AttachSandboxProvider(context.Context, *AttachSandboxProviderRequest) (*AttachSandboxProviderResponse, error) - // Detach a provider record from an existing sandbox. - DetachSandboxProvider(context.Context, *DetachSandboxProviderRequest) (*DetachSandboxProviderResponse, error) - // Inspect the installed authority for one sandbox provider mutation. - GetSandboxProviderStatus(context.Context, *GetSandboxProviderStatusRequest) (*GetSandboxProviderStatusResponse, error) - // Delete a sandbox by name. - DeleteSandbox(context.Context, *DeleteSandboxRequest) (*DeleteSandboxResponse, error) - // Stop a sandbox while retaining its persistent state. - StopSandbox(context.Context, *StopSandboxRequest) (*SandboxResponse, error) - // Start a previously stopped sandbox. - StartSandbox(context.Context, *StartSandboxRequest) (*SandboxResponse, error) - // Freeze a ready sandbox and retain its memory state for a later resume. - // Requires a compute driver and isolation backend that support state - // retention. - SuspendSandbox(context.Context, *SuspendSandboxRequest) (*SandboxResponse, error) - // Restore a suspended sandbox in a new runtime generation. - ResumeSandbox(context.Context, *ResumeSandboxRequest) (*SandboxResponse, error) - // Create a short-lived SSH session for a sandbox. - CreateSshSession(context.Context, *CreateSshSessionRequest) (*CreateSshSessionResponse, error) - // Create or update a sandbox HTTP service endpoint for local routing. - ExposeService(context.Context, *ExposeServiceRequest) (*ServiceEndpointResponse, error) - // Fetch one sandbox HTTP service endpoint. - GetService(context.Context, *GetServiceRequest) (*ServiceEndpointResponse, error) - // List sandbox HTTP service endpoints. - ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error) - // Delete one sandbox HTTP service endpoint. - DeleteService(context.Context, *DeleteServiceRequest) (*DeleteServiceResponse, error) - // Revoke a previously issued SSH session. - RevokeSshSession(context.Context, *RevokeSshSessionRequest) (*RevokeSshSessionResponse, error) - // Execute a command in a ready sandbox and stream output. - ExecSandbox(*ExecSandboxRequest, grpc.ServerStreamingServer[ExecSandboxEvent]) error - // Forward one CLI-side TCP connection to a loopback TCP target in a sandbox. - ForwardTcp(grpc.BidiStreamingServer[TcpForwardFrame, TcpForwardFrame]) error - // Execute an interactive command with bidirectional stdin/stdout streaming. - // The first client message MUST carry an ExecSandboxInput with the start - // variant. Subsequent messages carry stdin bytes or window resize events. - ExecSandboxInteractive(grpc.BidiStreamingServer[ExecSandboxInput, ExecSandboxEvent]) error - // Create a provider. - CreateProvider(context.Context, *CreateProviderRequest) (*ProviderResponse, error) - // Fetch a provider by name. - GetProvider(context.Context, *GetProviderRequest) (*ProviderResponse, error) - // Export selected usable runtime credentials. Only directly authenticated - // mTLS operators may call this method; refresh material is never returned. - GetProviderCredentials(context.Context, *GetProviderCredentialsRequest) (*GetProviderCredentialsResponse, error) - // List providers. - ListProviders(context.Context, *ListProvidersRequest) (*ListProvidersResponse, error) - // List available provider type profiles. - ListProviderProfiles(context.Context, *ListProviderProfilesRequest) (*ListProviderProfilesResponse, error) - // Fetch one provider type profile by id. - GetProviderProfile(context.Context, *GetProviderProfileRequest) (*ProviderProfileResponse, error) - // Import custom provider type profiles. - ImportProviderProfiles(context.Context, *ImportProviderProfilesRequest) (*ImportProviderProfilesResponse, error) - // Update an existing custom provider type profile. - UpdateProviderProfiles(context.Context, *UpdateProviderProfilesRequest) (*UpdateProviderProfilesResponse, error) - // Validate provider type profiles without registering them. - LintProviderProfiles(context.Context, *LintProviderProfilesRequest) (*LintProviderProfilesResponse, error) - // Update an existing provider by name. - UpdateProvider(context.Context, *UpdateProviderRequest) (*ProviderResponse, error) - // Fetch refresh status for one provider or provider credential. - GetProviderRefreshStatus(context.Context, *GetProviderRefreshStatusRequest) (*GetProviderRefreshStatusResponse, error) - // Configure gateway-owned refresh material for one provider credential. - ConfigureProviderRefresh(context.Context, *ConfigureProviderRefreshRequest) (*ConfigureProviderRefreshResponse, error) - // Record a gateway-owned refresh request for one provider credential. - RotateProviderCredential(context.Context, *RotateProviderCredentialRequest) (*RotateProviderCredentialResponse, error) - // Delete gateway-owned refresh configuration for one provider credential. - DeleteProviderRefresh(context.Context, *DeleteProviderRefreshRequest) (*DeleteProviderRefreshResponse, error) - // Delete a provider by name. - DeleteProvider(context.Context, *DeleteProviderRequest) (*DeleteProviderResponse, error) - // Delete a custom provider type profile by id. - DeleteProviderProfile(context.Context, *DeleteProviderProfileRequest) (*DeleteProviderProfileResponse, error) - // Get sandbox settings by id (called by users, and by supervisors at startup - // and in their poll loop). A supervisor whose session pushes configuration - // receives later updates over ConnectSupervisor and stops polling. - GetSandboxConfig(context.Context, *sandboxv1.GetSandboxConfigRequest) (*sandboxv1.GetSandboxConfigResponse, error) - // Get gateway-global settings (read-only runtime configuration; any - // authenticated user may read these without requiring Platform Admin). - // - // Scope-only (no role): scopes are granted by the IdP at token issuance, - // orthogonal to workspace membership. Deployments that enable scope - // enforcement configure the IdP to grant config:read (or openshell:all) - // to all sandbox users, so this does not block least-privilege flows. - GetGatewayConfig(context.Context, *sandboxv1.GetGatewayConfigRequest) (*sandboxv1.GetGatewayConfigResponse, error) - // Update settings or policy at sandbox or global scope. - UpdateConfig(context.Context, *UpdateConfigRequest) (*UpdateConfigResponse, error) - // Get the load status of a specific policy version. - GetSandboxPolicyStatus(context.Context, *GetSandboxPolicyStatusRequest) (*GetSandboxPolicyStatusResponse, error) - // List policy history for a sandbox. - ListSandboxPolicies(context.Context, *ListSandboxPoliciesRequest) (*ListSandboxPoliciesResponse, error) - // Report policy load result (called by sandbox after reload attempt). - ReportPolicyStatus(context.Context, *ReportPolicyStatusRequest) (*ReportPolicyStatusResponse, error) - // Replace the gateway's observed tool server endpoint status for one sandbox. - ReportEndpointStatus(context.Context, *ReportEndpointStatusRequest) (*ReportEndpointStatusResponse, error) - // Report installed provider state for the current ConnectSupervisor session. - // Replacing or losing that session invalidates its observations. - ReportProviderReadiness(context.Context, *ReportProviderReadinessRequest) (*ReportProviderReadinessResponse, error) - // Register startup and acknowledge an exact validated runtime configuration. - ReportSandboxConfiguration(context.Context, *ReportSandboxConfigurationRequest) (*ReportSandboxConfigurationResponse, error) - // Get provider environment for a sandbox (called by the sandbox supervisor at - // startup and by its poll loop). Polling projection of the provider - // environment delivered over ConnectSupervisor; removed with the polling RPCs. - GetSandboxProviderEnvironment(context.Context, *GetSandboxProviderEnvironmentRequest) (*GetSandboxProviderEnvironmentResponse, error) - // Exchange a stored provider subject token for an intermediate token scoped - // to the calling supervisor's SPIFFE identity. - ExchangeProviderSubjectToken(context.Context, *ExchangeProviderSubjectTokenRequest) (*ExchangeProviderSubjectTokenResponse, error) - // Fetch recent sandbox logs (one-shot). - GetSandboxLogs(context.Context, *GetSandboxLogsRequest) (*GetSandboxLogsResponse, error) - // Push sandbox supervisor logs to the server (client-streaming). - PushSandboxLogs(grpc.ClientStreamingServer[PushSandboxLogsRequest, PushSandboxLogsResponse]) error - // Persistent supervisor-to-gateway session (bidirectional streaming). - // - // The supervisor opens this stream at startup and keeps it alive for the - // sandbox lifetime. The gateway uses it to coordinate relay channels for - // SSH connect, ExecSandbox, targetable sandbox services, and configuration - // delivery. Supervisors advertise optional stream features in - // SupervisorHello, and the gateway sends only payloads the supervisor - // advertises. Raw service bytes flow over RelayStream calls (separate HTTP/2 - // streams on the same connection), not over this stream. - ConnectSupervisor(grpc.BidiStreamingServer[SupervisorMessage, GatewayMessage]) error - // Persist the canonical main process result before the supervisor exits. - ReportMainProcessExit(context.Context, *ReportMainProcessExitRequest) (*ReportMainProcessExitResponse, error) - // Confirm that foreground terminal delivery completed naturally. - FinalizeMainProcessExit(context.Context, *FinalizeMainProcessExitRequest) (*FinalizeMainProcessExitResponse, error) - // Raw byte relay between supervisor and gateway. - // - // The supervisor initiates this call after receiving a RelayOpen message - // on its ConnectSupervisor stream. The first RelayFrame carries a - // RelayInit with the channel_id to associate the new HTTP/2 stream with - // the pending relay slot on the gateway. Subsequent frames carry raw bytes in either - // direction between the gateway-side waiter (ForwardTcp / exec handler) - // and the supervisor-side target bridge. - // - // This rides the same TCP+TLS+HTTP/2 connection as ConnectSupervisor — - // no new TLS handshake, no reverse HTTP CONNECT. - RelayStream(grpc.BidiStreamingServer[RelayFrame, RelayFrame]) error - // Internal gateway-to-gateway relay forwarding. - // - // A gateway replica that receives a user request for a sandbox whose - // supervisor session is owned by a different replica opens this stream to the - // owner. The first frame carries PeerRelayInit; subsequent frames carry raw - // bytes in either direction. This RPC is authenticated as a gateway peer, not - // as a user or sandbox supervisor. - PeerRelay(grpc.BidiStreamingServer[PeerRelayFrame, PeerRelayFrame]) error - // Internal gateway-to-owner forwarding for supervisor-bound state. - PeerReportProviderReadiness(context.Context, *ReportProviderReadinessRequest) (*ReportProviderReadinessResponse, error) - PeerReportEndpointStatus(context.Context, *ReportEndpointStatusRequest) (*ReportEndpointStatusResponse, error) - PeerGetSandboxProviderStatus(context.Context, *GetSandboxProviderStatusRequest) (*GetSandboxProviderStatusResponse, error) - // Best-effort notification to the replica that owns a supervisor session. - // The receiver rebuilds snapshots from authoritative state. - PeerNotifyConfigUpdate(context.Context, *PeerNotifyConfigUpdateRequest) (*PeerNotifyConfigUpdateResponse, error) - // Watch a sandbox and stream updates. - // - // This stream can include: - // - Sandbox status snapshots (phase/status) - // - OpenShell server process logs correlated by sandbox_id - // - Platform events correlated to the sandbox - WatchSandbox(*WatchSandboxRequest, grpc.ServerStreamingServer[SandboxStreamEvent]) error - // Submit denial analysis results from sandbox (summaries + proposed chunks). - SubmitPolicyAnalysis(context.Context, *SubmitPolicyAnalysisRequest) (*SubmitPolicyAnalysisResponse, error) - // Get draft policy recommendations for a sandbox. - GetDraftPolicy(context.Context, *GetDraftPolicyRequest) (*GetDraftPolicyResponse, error) - // Approve a single draft policy chunk (merges into active policy). - ApproveDraftChunk(context.Context, *ApproveDraftChunkRequest) (*ApproveDraftChunkResponse, error) - // Reject a single draft policy chunk. - RejectDraftChunk(context.Context, *RejectDraftChunkRequest) (*RejectDraftChunkResponse, error) - // Approve all pending draft chunks (skips security-flagged unless forced). - ApproveAllDraftChunks(context.Context, *ApproveAllDraftChunksRequest) (*ApproveAllDraftChunksResponse, error) - // Edit a pending draft chunk in-place (e.g. narrow allowed_ips). - EditDraftChunk(context.Context, *EditDraftChunkRequest) (*EditDraftChunkResponse, error) - // Reverse an approval (remove merged rule from active policy). - UndoDraftChunk(context.Context, *UndoDraftChunkRequest) (*UndoDraftChunkResponse, error) - // Clear all pending draft chunks for a sandbox. - ClearDraftChunks(context.Context, *ClearDraftChunksRequest) (*ClearDraftChunksResponse, error) - // Get decision history for a sandbox's draft policy. - GetDraftHistory(context.Context, *GetDraftHistoryRequest) (*GetDraftHistoryResponse, error) - // Exchange a sandbox-bootstrap credential (e.g. a Kubernetes projected - // ServiceAccount token) for a gateway-minted JWT bound to the calling - // sandbox's UUID. Used by the Kubernetes driver path; singleplayer - // drivers receive the gateway JWT directly from the create-sandbox flow - // and never call this RPC. - IssueSandboxToken(context.Context, *IssueSandboxTokenRequest) (*IssueSandboxTokenResponse, error) - // Renew the calling sandbox's gateway JWT. Older tokens remain valid - // until their own expiry; deployments should keep token TTLs short to - // bound replay exposure. The supervisor calls this from a background - // task at ~80% of the token's lifetime; the new token is cached in - // memory only — the on-disk bootstrap file is intentionally not - // rewritten. - RefreshSandboxToken(context.Context, *RefreshSandboxTokenRequest) (*RefreshSandboxTokenResponse, error) - // Create a workspace. - CreateWorkspace(context.Context, *CreateWorkspaceRequest) (*CreateWorkspaceResponse, error) - // Fetch a workspace by name. - GetWorkspace(context.Context, *GetWorkspaceRequest) (*GetWorkspaceResponse, error) - // List workspaces. - ListWorkspaces(context.Context, *ListWorkspacesRequest) (*ListWorkspacesResponse, error) - // Delete a workspace by name. - DeleteWorkspace(context.Context, *DeleteWorkspaceRequest) (*DeleteWorkspaceResponse, error) - // Add a member to a workspace. - AddWorkspaceMember(context.Context, *AddWorkspaceMemberRequest) (*AddWorkspaceMemberResponse, error) - // Remove a member from a workspace. - RemoveWorkspaceMember(context.Context, *RemoveWorkspaceMemberRequest) (*RemoveWorkspaceMemberResponse, error) - // List members of a workspace. - ListWorkspaceMembers(context.Context, *ListWorkspaceMembersRequest) (*ListWorkspaceMembersResponse, error) - mustEmbedUnimplementedOpenShellServer() -} - -// UnimplementedOpenShellServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedOpenShellServer struct{} - -func (UnimplementedOpenShellServer) Health(context.Context, *HealthRequest) (*HealthResponse, error) { - return nil, status.Error(codes.Unimplemented, "method Health not implemented") -} -func (UnimplementedOpenShellServer) GetCurrentUser(context.Context, *GetCurrentUserRequest) (*GetCurrentUserResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetCurrentUser not implemented") -} -func (UnimplementedOpenShellServer) GetGatewayInfo(context.Context, *GetGatewayInfoRequest) (*GetGatewayInfoResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetGatewayInfo not implemented") -} -func (UnimplementedOpenShellServer) CreateSandbox(context.Context, *CreateSandboxRequest) (*SandboxResponse, error) { - return nil, status.Error(codes.Unimplemented, "method CreateSandbox not implemented") -} -func (UnimplementedOpenShellServer) BeginRootfsTarStaging(context.Context, *BeginRootfsTarStagingRequest) (*BeginRootfsTarStagingResponse, error) { - return nil, status.Error(codes.Unimplemented, "method BeginRootfsTarStaging not implemented") -} -func (UnimplementedOpenShellServer) GetSandbox(context.Context, *GetSandboxRequest) (*SandboxResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetSandbox not implemented") -} -func (UnimplementedOpenShellServer) ListSandboxes(context.Context, *ListSandboxesRequest) (*ListSandboxesResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ListSandboxes not implemented") -} -func (UnimplementedOpenShellServer) CreateSandboxTemplate(context.Context, *CreateSandboxTemplateRequest) (*SandboxTemplateResponse, error) { - return nil, status.Error(codes.Unimplemented, "method CreateSandboxTemplate not implemented") -} -func (UnimplementedOpenShellServer) GetSandboxTemplate(context.Context, *GetSandboxTemplateRequest) (*SandboxTemplateResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetSandboxTemplate not implemented") -} -func (UnimplementedOpenShellServer) ListSandboxTemplates(context.Context, *ListSandboxTemplatesRequest) (*ListSandboxTemplatesResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ListSandboxTemplates not implemented") -} -func (UnimplementedOpenShellServer) DeleteSandboxTemplate(context.Context, *DeleteSandboxTemplateRequest) (*DeleteSandboxTemplateResponse, error) { - return nil, status.Error(codes.Unimplemented, "method DeleteSandboxTemplate not implemented") -} -func (UnimplementedOpenShellServer) ListSandboxProviders(context.Context, *ListSandboxProvidersRequest) (*ListSandboxProvidersResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ListSandboxProviders not implemented") -} -func (UnimplementedOpenShellServer) AttachSandboxProvider(context.Context, *AttachSandboxProviderRequest) (*AttachSandboxProviderResponse, error) { - return nil, status.Error(codes.Unimplemented, "method AttachSandboxProvider not implemented") -} -func (UnimplementedOpenShellServer) DetachSandboxProvider(context.Context, *DetachSandboxProviderRequest) (*DetachSandboxProviderResponse, error) { - return nil, status.Error(codes.Unimplemented, "method DetachSandboxProvider not implemented") -} -func (UnimplementedOpenShellServer) GetSandboxProviderStatus(context.Context, *GetSandboxProviderStatusRequest) (*GetSandboxProviderStatusResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetSandboxProviderStatus not implemented") -} -func (UnimplementedOpenShellServer) DeleteSandbox(context.Context, *DeleteSandboxRequest) (*DeleteSandboxResponse, error) { - return nil, status.Error(codes.Unimplemented, "method DeleteSandbox not implemented") -} -func (UnimplementedOpenShellServer) StopSandbox(context.Context, *StopSandboxRequest) (*SandboxResponse, error) { - return nil, status.Error(codes.Unimplemented, "method StopSandbox not implemented") -} -func (UnimplementedOpenShellServer) StartSandbox(context.Context, *StartSandboxRequest) (*SandboxResponse, error) { - return nil, status.Error(codes.Unimplemented, "method StartSandbox not implemented") -} -func (UnimplementedOpenShellServer) SuspendSandbox(context.Context, *SuspendSandboxRequest) (*SandboxResponse, error) { - return nil, status.Error(codes.Unimplemented, "method SuspendSandbox not implemented") -} -func (UnimplementedOpenShellServer) ResumeSandbox(context.Context, *ResumeSandboxRequest) (*SandboxResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ResumeSandbox not implemented") -} -func (UnimplementedOpenShellServer) CreateSshSession(context.Context, *CreateSshSessionRequest) (*CreateSshSessionResponse, error) { - return nil, status.Error(codes.Unimplemented, "method CreateSshSession not implemented") -} -func (UnimplementedOpenShellServer) ExposeService(context.Context, *ExposeServiceRequest) (*ServiceEndpointResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ExposeService not implemented") -} -func (UnimplementedOpenShellServer) GetService(context.Context, *GetServiceRequest) (*ServiceEndpointResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetService not implemented") -} -func (UnimplementedOpenShellServer) ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ListServices not implemented") -} -func (UnimplementedOpenShellServer) DeleteService(context.Context, *DeleteServiceRequest) (*DeleteServiceResponse, error) { - return nil, status.Error(codes.Unimplemented, "method DeleteService not implemented") -} -func (UnimplementedOpenShellServer) RevokeSshSession(context.Context, *RevokeSshSessionRequest) (*RevokeSshSessionResponse, error) { - return nil, status.Error(codes.Unimplemented, "method RevokeSshSession not implemented") -} -func (UnimplementedOpenShellServer) ExecSandbox(*ExecSandboxRequest, grpc.ServerStreamingServer[ExecSandboxEvent]) error { - return status.Error(codes.Unimplemented, "method ExecSandbox not implemented") -} -func (UnimplementedOpenShellServer) ForwardTcp(grpc.BidiStreamingServer[TcpForwardFrame, TcpForwardFrame]) error { - return status.Error(codes.Unimplemented, "method ForwardTcp not implemented") -} -func (UnimplementedOpenShellServer) ExecSandboxInteractive(grpc.BidiStreamingServer[ExecSandboxInput, ExecSandboxEvent]) error { - return status.Error(codes.Unimplemented, "method ExecSandboxInteractive not implemented") -} -func (UnimplementedOpenShellServer) CreateProvider(context.Context, *CreateProviderRequest) (*ProviderResponse, error) { - return nil, status.Error(codes.Unimplemented, "method CreateProvider not implemented") -} -func (UnimplementedOpenShellServer) GetProvider(context.Context, *GetProviderRequest) (*ProviderResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetProvider not implemented") -} -func (UnimplementedOpenShellServer) GetProviderCredentials(context.Context, *GetProviderCredentialsRequest) (*GetProviderCredentialsResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetProviderCredentials not implemented") -} -func (UnimplementedOpenShellServer) ListProviders(context.Context, *ListProvidersRequest) (*ListProvidersResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ListProviders not implemented") -} -func (UnimplementedOpenShellServer) ListProviderProfiles(context.Context, *ListProviderProfilesRequest) (*ListProviderProfilesResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ListProviderProfiles not implemented") -} -func (UnimplementedOpenShellServer) GetProviderProfile(context.Context, *GetProviderProfileRequest) (*ProviderProfileResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetProviderProfile not implemented") -} -func (UnimplementedOpenShellServer) ImportProviderProfiles(context.Context, *ImportProviderProfilesRequest) (*ImportProviderProfilesResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ImportProviderProfiles not implemented") -} -func (UnimplementedOpenShellServer) UpdateProviderProfiles(context.Context, *UpdateProviderProfilesRequest) (*UpdateProviderProfilesResponse, error) { - return nil, status.Error(codes.Unimplemented, "method UpdateProviderProfiles not implemented") -} -func (UnimplementedOpenShellServer) LintProviderProfiles(context.Context, *LintProviderProfilesRequest) (*LintProviderProfilesResponse, error) { - return nil, status.Error(codes.Unimplemented, "method LintProviderProfiles not implemented") -} -func (UnimplementedOpenShellServer) UpdateProvider(context.Context, *UpdateProviderRequest) (*ProviderResponse, error) { - return nil, status.Error(codes.Unimplemented, "method UpdateProvider not implemented") -} -func (UnimplementedOpenShellServer) GetProviderRefreshStatus(context.Context, *GetProviderRefreshStatusRequest) (*GetProviderRefreshStatusResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetProviderRefreshStatus not implemented") -} -func (UnimplementedOpenShellServer) ConfigureProviderRefresh(context.Context, *ConfigureProviderRefreshRequest) (*ConfigureProviderRefreshResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ConfigureProviderRefresh not implemented") -} -func (UnimplementedOpenShellServer) RotateProviderCredential(context.Context, *RotateProviderCredentialRequest) (*RotateProviderCredentialResponse, error) { - return nil, status.Error(codes.Unimplemented, "method RotateProviderCredential not implemented") -} -func (UnimplementedOpenShellServer) DeleteProviderRefresh(context.Context, *DeleteProviderRefreshRequest) (*DeleteProviderRefreshResponse, error) { - return nil, status.Error(codes.Unimplemented, "method DeleteProviderRefresh not implemented") -} -func (UnimplementedOpenShellServer) DeleteProvider(context.Context, *DeleteProviderRequest) (*DeleteProviderResponse, error) { - return nil, status.Error(codes.Unimplemented, "method DeleteProvider not implemented") -} -func (UnimplementedOpenShellServer) DeleteProviderProfile(context.Context, *DeleteProviderProfileRequest) (*DeleteProviderProfileResponse, error) { - return nil, status.Error(codes.Unimplemented, "method DeleteProviderProfile not implemented") -} -func (UnimplementedOpenShellServer) GetSandboxConfig(context.Context, *sandboxv1.GetSandboxConfigRequest) (*sandboxv1.GetSandboxConfigResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetSandboxConfig not implemented") -} -func (UnimplementedOpenShellServer) GetGatewayConfig(context.Context, *sandboxv1.GetGatewayConfigRequest) (*sandboxv1.GetGatewayConfigResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetGatewayConfig not implemented") -} -func (UnimplementedOpenShellServer) UpdateConfig(context.Context, *UpdateConfigRequest) (*UpdateConfigResponse, error) { - return nil, status.Error(codes.Unimplemented, "method UpdateConfig not implemented") -} -func (UnimplementedOpenShellServer) GetSandboxPolicyStatus(context.Context, *GetSandboxPolicyStatusRequest) (*GetSandboxPolicyStatusResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetSandboxPolicyStatus not implemented") -} -func (UnimplementedOpenShellServer) ListSandboxPolicies(context.Context, *ListSandboxPoliciesRequest) (*ListSandboxPoliciesResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ListSandboxPolicies not implemented") -} -func (UnimplementedOpenShellServer) ReportPolicyStatus(context.Context, *ReportPolicyStatusRequest) (*ReportPolicyStatusResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ReportPolicyStatus not implemented") -} -func (UnimplementedOpenShellServer) ReportEndpointStatus(context.Context, *ReportEndpointStatusRequest) (*ReportEndpointStatusResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ReportEndpointStatus not implemented") -} -func (UnimplementedOpenShellServer) ReportProviderReadiness(context.Context, *ReportProviderReadinessRequest) (*ReportProviderReadinessResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ReportProviderReadiness not implemented") -} -func (UnimplementedOpenShellServer) ReportSandboxConfiguration(context.Context, *ReportSandboxConfigurationRequest) (*ReportSandboxConfigurationResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ReportSandboxConfiguration not implemented") -} -func (UnimplementedOpenShellServer) GetSandboxProviderEnvironment(context.Context, *GetSandboxProviderEnvironmentRequest) (*GetSandboxProviderEnvironmentResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetSandboxProviderEnvironment not implemented") -} -func (UnimplementedOpenShellServer) ExchangeProviderSubjectToken(context.Context, *ExchangeProviderSubjectTokenRequest) (*ExchangeProviderSubjectTokenResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ExchangeProviderSubjectToken not implemented") -} -func (UnimplementedOpenShellServer) GetSandboxLogs(context.Context, *GetSandboxLogsRequest) (*GetSandboxLogsResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetSandboxLogs not implemented") -} -func (UnimplementedOpenShellServer) PushSandboxLogs(grpc.ClientStreamingServer[PushSandboxLogsRequest, PushSandboxLogsResponse]) error { - return status.Error(codes.Unimplemented, "method PushSandboxLogs not implemented") -} -func (UnimplementedOpenShellServer) ConnectSupervisor(grpc.BidiStreamingServer[SupervisorMessage, GatewayMessage]) error { - return status.Error(codes.Unimplemented, "method ConnectSupervisor not implemented") -} -func (UnimplementedOpenShellServer) ReportMainProcessExit(context.Context, *ReportMainProcessExitRequest) (*ReportMainProcessExitResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ReportMainProcessExit not implemented") -} -func (UnimplementedOpenShellServer) FinalizeMainProcessExit(context.Context, *FinalizeMainProcessExitRequest) (*FinalizeMainProcessExitResponse, error) { - return nil, status.Error(codes.Unimplemented, "method FinalizeMainProcessExit not implemented") -} -func (UnimplementedOpenShellServer) RelayStream(grpc.BidiStreamingServer[RelayFrame, RelayFrame]) error { - return status.Error(codes.Unimplemented, "method RelayStream not implemented") -} -func (UnimplementedOpenShellServer) PeerRelay(grpc.BidiStreamingServer[PeerRelayFrame, PeerRelayFrame]) error { - return status.Error(codes.Unimplemented, "method PeerRelay not implemented") -} -func (UnimplementedOpenShellServer) PeerReportProviderReadiness(context.Context, *ReportProviderReadinessRequest) (*ReportProviderReadinessResponse, error) { - return nil, status.Error(codes.Unimplemented, "method PeerReportProviderReadiness not implemented") -} -func (UnimplementedOpenShellServer) PeerReportEndpointStatus(context.Context, *ReportEndpointStatusRequest) (*ReportEndpointStatusResponse, error) { - return nil, status.Error(codes.Unimplemented, "method PeerReportEndpointStatus not implemented") -} -func (UnimplementedOpenShellServer) PeerGetSandboxProviderStatus(context.Context, *GetSandboxProviderStatusRequest) (*GetSandboxProviderStatusResponse, error) { - return nil, status.Error(codes.Unimplemented, "method PeerGetSandboxProviderStatus not implemented") -} -func (UnimplementedOpenShellServer) PeerNotifyConfigUpdate(context.Context, *PeerNotifyConfigUpdateRequest) (*PeerNotifyConfigUpdateResponse, error) { - return nil, status.Error(codes.Unimplemented, "method PeerNotifyConfigUpdate not implemented") -} -func (UnimplementedOpenShellServer) WatchSandbox(*WatchSandboxRequest, grpc.ServerStreamingServer[SandboxStreamEvent]) error { - return status.Error(codes.Unimplemented, "method WatchSandbox not implemented") -} -func (UnimplementedOpenShellServer) SubmitPolicyAnalysis(context.Context, *SubmitPolicyAnalysisRequest) (*SubmitPolicyAnalysisResponse, error) { - return nil, status.Error(codes.Unimplemented, "method SubmitPolicyAnalysis not implemented") -} -func (UnimplementedOpenShellServer) GetDraftPolicy(context.Context, *GetDraftPolicyRequest) (*GetDraftPolicyResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetDraftPolicy not implemented") -} -func (UnimplementedOpenShellServer) ApproveDraftChunk(context.Context, *ApproveDraftChunkRequest) (*ApproveDraftChunkResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ApproveDraftChunk not implemented") -} -func (UnimplementedOpenShellServer) RejectDraftChunk(context.Context, *RejectDraftChunkRequest) (*RejectDraftChunkResponse, error) { - return nil, status.Error(codes.Unimplemented, "method RejectDraftChunk not implemented") -} -func (UnimplementedOpenShellServer) ApproveAllDraftChunks(context.Context, *ApproveAllDraftChunksRequest) (*ApproveAllDraftChunksResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ApproveAllDraftChunks not implemented") -} -func (UnimplementedOpenShellServer) EditDraftChunk(context.Context, *EditDraftChunkRequest) (*EditDraftChunkResponse, error) { - return nil, status.Error(codes.Unimplemented, "method EditDraftChunk not implemented") -} -func (UnimplementedOpenShellServer) UndoDraftChunk(context.Context, *UndoDraftChunkRequest) (*UndoDraftChunkResponse, error) { - return nil, status.Error(codes.Unimplemented, "method UndoDraftChunk not implemented") -} -func (UnimplementedOpenShellServer) ClearDraftChunks(context.Context, *ClearDraftChunksRequest) (*ClearDraftChunksResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ClearDraftChunks not implemented") -} -func (UnimplementedOpenShellServer) GetDraftHistory(context.Context, *GetDraftHistoryRequest) (*GetDraftHistoryResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetDraftHistory not implemented") -} -func (UnimplementedOpenShellServer) IssueSandboxToken(context.Context, *IssueSandboxTokenRequest) (*IssueSandboxTokenResponse, error) { - return nil, status.Error(codes.Unimplemented, "method IssueSandboxToken not implemented") -} -func (UnimplementedOpenShellServer) RefreshSandboxToken(context.Context, *RefreshSandboxTokenRequest) (*RefreshSandboxTokenResponse, error) { - return nil, status.Error(codes.Unimplemented, "method RefreshSandboxToken not implemented") -} -func (UnimplementedOpenShellServer) CreateWorkspace(context.Context, *CreateWorkspaceRequest) (*CreateWorkspaceResponse, error) { - return nil, status.Error(codes.Unimplemented, "method CreateWorkspace not implemented") -} -func (UnimplementedOpenShellServer) GetWorkspace(context.Context, *GetWorkspaceRequest) (*GetWorkspaceResponse, error) { - return nil, status.Error(codes.Unimplemented, "method GetWorkspace not implemented") -} -func (UnimplementedOpenShellServer) ListWorkspaces(context.Context, *ListWorkspacesRequest) (*ListWorkspacesResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ListWorkspaces not implemented") -} -func (UnimplementedOpenShellServer) DeleteWorkspace(context.Context, *DeleteWorkspaceRequest) (*DeleteWorkspaceResponse, error) { - return nil, status.Error(codes.Unimplemented, "method DeleteWorkspace not implemented") -} -func (UnimplementedOpenShellServer) AddWorkspaceMember(context.Context, *AddWorkspaceMemberRequest) (*AddWorkspaceMemberResponse, error) { - return nil, status.Error(codes.Unimplemented, "method AddWorkspaceMember not implemented") -} -func (UnimplementedOpenShellServer) RemoveWorkspaceMember(context.Context, *RemoveWorkspaceMemberRequest) (*RemoveWorkspaceMemberResponse, error) { - return nil, status.Error(codes.Unimplemented, "method RemoveWorkspaceMember not implemented") -} -func (UnimplementedOpenShellServer) ListWorkspaceMembers(context.Context, *ListWorkspaceMembersRequest) (*ListWorkspaceMembersResponse, error) { - return nil, status.Error(codes.Unimplemented, "method ListWorkspaceMembers not implemented") -} -func (UnimplementedOpenShellServer) mustEmbedUnimplementedOpenShellServer() {} -func (UnimplementedOpenShellServer) testEmbeddedByValue() {} - -// UnsafeOpenShellServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to OpenShellServer will -// result in compilation errors. -type UnsafeOpenShellServer interface { - mustEmbedUnimplementedOpenShellServer() -} - -func RegisterOpenShellServer(s grpc.ServiceRegistrar, srv OpenShellServer) { - // If the following call panics, it indicates UnimplementedOpenShellServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } - s.RegisterService(&OpenShell_ServiceDesc, srv) -} - -func _OpenShell_Health_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(HealthRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).Health(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_Health_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).Health(ctx, req.(*HealthRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetCurrentUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetCurrentUserRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetCurrentUser(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetCurrentUser_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetCurrentUser(ctx, req.(*GetCurrentUserRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetGatewayInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetGatewayInfoRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetGatewayInfo(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetGatewayInfo_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetGatewayInfo(ctx, req.(*GetGatewayInfoRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_CreateSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).CreateSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_CreateSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).CreateSandbox(ctx, req.(*CreateSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_BeginRootfsTarStaging_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(BeginRootfsTarStagingRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).BeginRootfsTarStaging(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_BeginRootfsTarStaging_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).BeginRootfsTarStaging(ctx, req.(*BeginRootfsTarStagingRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandbox(ctx, req.(*GetSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListSandboxes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListSandboxesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListSandboxes(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListSandboxes_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListSandboxes(ctx, req.(*ListSandboxesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_CreateSandboxTemplate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateSandboxTemplateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).CreateSandboxTemplate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_CreateSandboxTemplate_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).CreateSandboxTemplate(ctx, req.(*CreateSandboxTemplateRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetSandboxTemplate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxTemplateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandboxTemplate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandboxTemplate_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandboxTemplate(ctx, req.(*GetSandboxTemplateRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListSandboxTemplates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListSandboxTemplatesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListSandboxTemplates(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListSandboxTemplates_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListSandboxTemplates(ctx, req.(*ListSandboxTemplatesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DeleteSandboxTemplate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteSandboxTemplateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DeleteSandboxTemplate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DeleteSandboxTemplate_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DeleteSandboxTemplate(ctx, req.(*DeleteSandboxTemplateRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListSandboxProviders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListSandboxProvidersRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListSandboxProviders(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListSandboxProviders_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListSandboxProviders(ctx, req.(*ListSandboxProvidersRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_AttachSandboxProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AttachSandboxProviderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).AttachSandboxProvider(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_AttachSandboxProvider_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).AttachSandboxProvider(ctx, req.(*AttachSandboxProviderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DetachSandboxProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DetachSandboxProviderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DetachSandboxProvider(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DetachSandboxProvider_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DetachSandboxProvider(ctx, req.(*DetachSandboxProviderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetSandboxProviderStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxProviderStatusRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandboxProviderStatus(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandboxProviderStatus_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandboxProviderStatus(ctx, req.(*GetSandboxProviderStatusRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DeleteSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DeleteSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DeleteSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DeleteSandbox(ctx, req.(*DeleteSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_StopSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(StopSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).StopSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_StopSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).StopSandbox(ctx, req.(*StopSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_StartSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(StartSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).StartSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_StartSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).StartSandbox(ctx, req.(*StartSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_SuspendSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SuspendSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).SuspendSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_SuspendSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).SuspendSandbox(ctx, req.(*SuspendSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ResumeSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ResumeSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ResumeSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ResumeSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ResumeSandbox(ctx, req.(*ResumeSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_CreateSshSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateSshSessionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).CreateSshSession(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_CreateSshSession_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).CreateSshSession(ctx, req.(*CreateSshSessionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ExposeService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ExposeServiceRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ExposeService(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ExposeService_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ExposeService(ctx, req.(*ExposeServiceRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetServiceRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetService(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetService_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetService(ctx, req.(*GetServiceRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListServicesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListServices(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListServices_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListServices(ctx, req.(*ListServicesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DeleteService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteServiceRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DeleteService(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DeleteService_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DeleteService(ctx, req.(*DeleteServiceRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_RevokeSshSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RevokeSshSessionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).RevokeSshSession(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_RevokeSshSession_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).RevokeSshSession(ctx, req.(*RevokeSshSessionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ExecSandbox_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(ExecSandboxRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(OpenShellServer).ExecSandbox(m, &grpc.GenericServerStream[ExecSandboxRequest, ExecSandboxEvent]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ExecSandboxServer = grpc.ServerStreamingServer[ExecSandboxEvent] - -func _OpenShell_ForwardTcp_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(OpenShellServer).ForwardTcp(&grpc.GenericServerStream[TcpForwardFrame, TcpForwardFrame]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ForwardTcpServer = grpc.BidiStreamingServer[TcpForwardFrame, TcpForwardFrame] - -func _OpenShell_ExecSandboxInteractive_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(OpenShellServer).ExecSandboxInteractive(&grpc.GenericServerStream[ExecSandboxInput, ExecSandboxEvent]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ExecSandboxInteractiveServer = grpc.BidiStreamingServer[ExecSandboxInput, ExecSandboxEvent] - -func _OpenShell_CreateProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateProviderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).CreateProvider(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_CreateProvider_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).CreateProvider(ctx, req.(*CreateProviderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetProviderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetProvider(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetProvider_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetProvider(ctx, req.(*GetProviderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetProviderCredentials_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetProviderCredentialsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetProviderCredentials(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetProviderCredentials_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetProviderCredentials(ctx, req.(*GetProviderCredentialsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListProviders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListProvidersRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListProviders(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListProviders_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListProviders(ctx, req.(*ListProvidersRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListProviderProfiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListProviderProfilesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListProviderProfiles(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListProviderProfiles_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListProviderProfiles(ctx, req.(*ListProviderProfilesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetProviderProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetProviderProfileRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetProviderProfile(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetProviderProfile_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetProviderProfile(ctx, req.(*GetProviderProfileRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ImportProviderProfiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ImportProviderProfilesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ImportProviderProfiles(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ImportProviderProfiles_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ImportProviderProfiles(ctx, req.(*ImportProviderProfilesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_UpdateProviderProfiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateProviderProfilesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).UpdateProviderProfiles(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_UpdateProviderProfiles_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).UpdateProviderProfiles(ctx, req.(*UpdateProviderProfilesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_LintProviderProfiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(LintProviderProfilesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).LintProviderProfiles(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_LintProviderProfiles_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).LintProviderProfiles(ctx, req.(*LintProviderProfilesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_UpdateProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateProviderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).UpdateProvider(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_UpdateProvider_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).UpdateProvider(ctx, req.(*UpdateProviderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetProviderRefreshStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetProviderRefreshStatusRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetProviderRefreshStatus(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetProviderRefreshStatus_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetProviderRefreshStatus(ctx, req.(*GetProviderRefreshStatusRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ConfigureProviderRefresh_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ConfigureProviderRefreshRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ConfigureProviderRefresh(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ConfigureProviderRefresh_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ConfigureProviderRefresh(ctx, req.(*ConfigureProviderRefreshRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_RotateProviderCredential_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RotateProviderCredentialRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).RotateProviderCredential(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_RotateProviderCredential_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).RotateProviderCredential(ctx, req.(*RotateProviderCredentialRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DeleteProviderRefresh_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteProviderRefreshRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DeleteProviderRefresh(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DeleteProviderRefresh_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DeleteProviderRefresh(ctx, req.(*DeleteProviderRefreshRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DeleteProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteProviderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DeleteProvider(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DeleteProvider_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DeleteProvider(ctx, req.(*DeleteProviderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DeleteProviderProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteProviderProfileRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DeleteProviderProfile(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DeleteProviderProfile_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DeleteProviderProfile(ctx, req.(*DeleteProviderProfileRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetSandboxConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(sandboxv1.GetSandboxConfigRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandboxConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandboxConfig_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandboxConfig(ctx, req.(*sandboxv1.GetSandboxConfigRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetGatewayConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(sandboxv1.GetGatewayConfigRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetGatewayConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetGatewayConfig_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetGatewayConfig(ctx, req.(*sandboxv1.GetGatewayConfigRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_UpdateConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateConfigRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).UpdateConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_UpdateConfig_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).UpdateConfig(ctx, req.(*UpdateConfigRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetSandboxPolicyStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxPolicyStatusRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandboxPolicyStatus(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandboxPolicyStatus_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandboxPolicyStatus(ctx, req.(*GetSandboxPolicyStatusRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListSandboxPolicies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListSandboxPoliciesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListSandboxPolicies(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListSandboxPolicies_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListSandboxPolicies(ctx, req.(*ListSandboxPoliciesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ReportPolicyStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReportPolicyStatusRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ReportPolicyStatus(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ReportPolicyStatus_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ReportPolicyStatus(ctx, req.(*ReportPolicyStatusRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ReportEndpointStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReportEndpointStatusRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ReportEndpointStatus(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ReportEndpointStatus_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ReportEndpointStatus(ctx, req.(*ReportEndpointStatusRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ReportProviderReadiness_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReportProviderReadinessRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ReportProviderReadiness(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ReportProviderReadiness_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ReportProviderReadiness(ctx, req.(*ReportProviderReadinessRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ReportSandboxConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReportSandboxConfigurationRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ReportSandboxConfiguration(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ReportSandboxConfiguration_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ReportSandboxConfiguration(ctx, req.(*ReportSandboxConfigurationRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetSandboxProviderEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxProviderEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandboxProviderEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandboxProviderEnvironment_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandboxProviderEnvironment(ctx, req.(*GetSandboxProviderEnvironmentRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ExchangeProviderSubjectToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ExchangeProviderSubjectTokenRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ExchangeProviderSubjectToken(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ExchangeProviderSubjectToken_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ExchangeProviderSubjectToken(ctx, req.(*ExchangeProviderSubjectTokenRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetSandboxLogs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxLogsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandboxLogs(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandboxLogs_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandboxLogs(ctx, req.(*GetSandboxLogsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_PushSandboxLogs_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(OpenShellServer).PushSandboxLogs(&grpc.GenericServerStream[PushSandboxLogsRequest, PushSandboxLogsResponse]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_PushSandboxLogsServer = grpc.ClientStreamingServer[PushSandboxLogsRequest, PushSandboxLogsResponse] - -func _OpenShell_ConnectSupervisor_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(OpenShellServer).ConnectSupervisor(&grpc.GenericServerStream[SupervisorMessage, GatewayMessage]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ConnectSupervisorServer = grpc.BidiStreamingServer[SupervisorMessage, GatewayMessage] - -func _OpenShell_ReportMainProcessExit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReportMainProcessExitRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ReportMainProcessExit(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ReportMainProcessExit_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ReportMainProcessExit(ctx, req.(*ReportMainProcessExitRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_FinalizeMainProcessExit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(FinalizeMainProcessExitRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).FinalizeMainProcessExit(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_FinalizeMainProcessExit_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).FinalizeMainProcessExit(ctx, req.(*FinalizeMainProcessExitRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_RelayStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(OpenShellServer).RelayStream(&grpc.GenericServerStream[RelayFrame, RelayFrame]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_RelayStreamServer = grpc.BidiStreamingServer[RelayFrame, RelayFrame] - -func _OpenShell_PeerRelay_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(OpenShellServer).PeerRelay(&grpc.GenericServerStream[PeerRelayFrame, PeerRelayFrame]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_PeerRelayServer = grpc.BidiStreamingServer[PeerRelayFrame, PeerRelayFrame] - -func _OpenShell_PeerReportProviderReadiness_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReportProviderReadinessRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).PeerReportProviderReadiness(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_PeerReportProviderReadiness_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).PeerReportProviderReadiness(ctx, req.(*ReportProviderReadinessRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_PeerReportEndpointStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReportEndpointStatusRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).PeerReportEndpointStatus(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_PeerReportEndpointStatus_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).PeerReportEndpointStatus(ctx, req.(*ReportEndpointStatusRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_PeerGetSandboxProviderStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxProviderStatusRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).PeerGetSandboxProviderStatus(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_PeerGetSandboxProviderStatus_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).PeerGetSandboxProviderStatus(ctx, req.(*GetSandboxProviderStatusRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_PeerNotifyConfigUpdate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PeerNotifyConfigUpdateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).PeerNotifyConfigUpdate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_PeerNotifyConfigUpdate_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).PeerNotifyConfigUpdate(ctx, req.(*PeerNotifyConfigUpdateRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_WatchSandbox_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(WatchSandboxRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(OpenShellServer).WatchSandbox(m, &grpc.GenericServerStream[WatchSandboxRequest, SandboxStreamEvent]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_WatchSandboxServer = grpc.ServerStreamingServer[SandboxStreamEvent] - -func _OpenShell_SubmitPolicyAnalysis_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SubmitPolicyAnalysisRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).SubmitPolicyAnalysis(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_SubmitPolicyAnalysis_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).SubmitPolicyAnalysis(ctx, req.(*SubmitPolicyAnalysisRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetDraftPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetDraftPolicyRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetDraftPolicy(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetDraftPolicy_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetDraftPolicy(ctx, req.(*GetDraftPolicyRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ApproveDraftChunk_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ApproveDraftChunkRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ApproveDraftChunk(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ApproveDraftChunk_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ApproveDraftChunk(ctx, req.(*ApproveDraftChunkRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_RejectDraftChunk_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RejectDraftChunkRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).RejectDraftChunk(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_RejectDraftChunk_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).RejectDraftChunk(ctx, req.(*RejectDraftChunkRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ApproveAllDraftChunks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ApproveAllDraftChunksRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ApproveAllDraftChunks(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ApproveAllDraftChunks_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ApproveAllDraftChunks(ctx, req.(*ApproveAllDraftChunksRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_EditDraftChunk_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(EditDraftChunkRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).EditDraftChunk(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_EditDraftChunk_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).EditDraftChunk(ctx, req.(*EditDraftChunkRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_UndoDraftChunk_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UndoDraftChunkRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).UndoDraftChunk(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_UndoDraftChunk_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).UndoDraftChunk(ctx, req.(*UndoDraftChunkRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ClearDraftChunks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ClearDraftChunksRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ClearDraftChunks(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ClearDraftChunks_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ClearDraftChunks(ctx, req.(*ClearDraftChunksRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetDraftHistory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetDraftHistoryRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetDraftHistory(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetDraftHistory_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetDraftHistory(ctx, req.(*GetDraftHistoryRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_IssueSandboxToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(IssueSandboxTokenRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).IssueSandboxToken(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_IssueSandboxToken_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).IssueSandboxToken(ctx, req.(*IssueSandboxTokenRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_RefreshSandboxToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RefreshSandboxTokenRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).RefreshSandboxToken(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_RefreshSandboxToken_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).RefreshSandboxToken(ctx, req.(*RefreshSandboxTokenRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_CreateWorkspace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateWorkspaceRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).CreateWorkspace(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_CreateWorkspace_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).CreateWorkspace(ctx, req.(*CreateWorkspaceRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetWorkspace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetWorkspaceRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetWorkspace(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetWorkspace_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetWorkspace(ctx, req.(*GetWorkspaceRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListWorkspaces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListWorkspacesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListWorkspaces(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListWorkspaces_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListWorkspaces(ctx, req.(*ListWorkspacesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DeleteWorkspace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteWorkspaceRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DeleteWorkspace(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DeleteWorkspace_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DeleteWorkspace(ctx, req.(*DeleteWorkspaceRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_AddWorkspaceMember_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AddWorkspaceMemberRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).AddWorkspaceMember(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_AddWorkspaceMember_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).AddWorkspaceMember(ctx, req.(*AddWorkspaceMemberRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_RemoveWorkspaceMember_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveWorkspaceMemberRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).RemoveWorkspaceMember(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_RemoveWorkspaceMember_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).RemoveWorkspaceMember(ctx, req.(*RemoveWorkspaceMemberRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListWorkspaceMembers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListWorkspaceMembersRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListWorkspaceMembers(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListWorkspaceMembers_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListWorkspaceMembers(ctx, req.(*ListWorkspaceMembersRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// OpenShell_ServiceDesc is the grpc.ServiceDesc for OpenShell service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var OpenShell_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "openshell.v1.OpenShell", - HandlerType: (*OpenShellServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Health", - Handler: _OpenShell_Health_Handler, - }, - { - MethodName: "GetCurrentUser", - Handler: _OpenShell_GetCurrentUser_Handler, - }, - { - MethodName: "GetGatewayInfo", - Handler: _OpenShell_GetGatewayInfo_Handler, - }, - { - MethodName: "CreateSandbox", - Handler: _OpenShell_CreateSandbox_Handler, - }, - { - MethodName: "BeginRootfsTarStaging", - Handler: _OpenShell_BeginRootfsTarStaging_Handler, - }, - { - MethodName: "GetSandbox", - Handler: _OpenShell_GetSandbox_Handler, - }, - { - MethodName: "ListSandboxes", - Handler: _OpenShell_ListSandboxes_Handler, - }, - { - MethodName: "CreateSandboxTemplate", - Handler: _OpenShell_CreateSandboxTemplate_Handler, - }, - { - MethodName: "GetSandboxTemplate", - Handler: _OpenShell_GetSandboxTemplate_Handler, - }, - { - MethodName: "ListSandboxTemplates", - Handler: _OpenShell_ListSandboxTemplates_Handler, - }, - { - MethodName: "DeleteSandboxTemplate", - Handler: _OpenShell_DeleteSandboxTemplate_Handler, - }, - { - MethodName: "ListSandboxProviders", - Handler: _OpenShell_ListSandboxProviders_Handler, - }, - { - MethodName: "AttachSandboxProvider", - Handler: _OpenShell_AttachSandboxProvider_Handler, - }, - { - MethodName: "DetachSandboxProvider", - Handler: _OpenShell_DetachSandboxProvider_Handler, - }, - { - MethodName: "GetSandboxProviderStatus", - Handler: _OpenShell_GetSandboxProviderStatus_Handler, - }, - { - MethodName: "DeleteSandbox", - Handler: _OpenShell_DeleteSandbox_Handler, - }, - { - MethodName: "StopSandbox", - Handler: _OpenShell_StopSandbox_Handler, - }, - { - MethodName: "StartSandbox", - Handler: _OpenShell_StartSandbox_Handler, - }, - { - MethodName: "SuspendSandbox", - Handler: _OpenShell_SuspendSandbox_Handler, - }, - { - MethodName: "ResumeSandbox", - Handler: _OpenShell_ResumeSandbox_Handler, - }, - { - MethodName: "CreateSshSession", - Handler: _OpenShell_CreateSshSession_Handler, - }, - { - MethodName: "ExposeService", - Handler: _OpenShell_ExposeService_Handler, - }, - { - MethodName: "GetService", - Handler: _OpenShell_GetService_Handler, - }, - { - MethodName: "ListServices", - Handler: _OpenShell_ListServices_Handler, - }, - { - MethodName: "DeleteService", - Handler: _OpenShell_DeleteService_Handler, - }, - { - MethodName: "RevokeSshSession", - Handler: _OpenShell_RevokeSshSession_Handler, - }, - { - MethodName: "CreateProvider", - Handler: _OpenShell_CreateProvider_Handler, - }, - { - MethodName: "GetProvider", - Handler: _OpenShell_GetProvider_Handler, - }, - { - MethodName: "GetProviderCredentials", - Handler: _OpenShell_GetProviderCredentials_Handler, - }, - { - MethodName: "ListProviders", - Handler: _OpenShell_ListProviders_Handler, - }, - { - MethodName: "ListProviderProfiles", - Handler: _OpenShell_ListProviderProfiles_Handler, - }, - { - MethodName: "GetProviderProfile", - Handler: _OpenShell_GetProviderProfile_Handler, - }, - { - MethodName: "ImportProviderProfiles", - Handler: _OpenShell_ImportProviderProfiles_Handler, - }, - { - MethodName: "UpdateProviderProfiles", - Handler: _OpenShell_UpdateProviderProfiles_Handler, - }, - { - MethodName: "LintProviderProfiles", - Handler: _OpenShell_LintProviderProfiles_Handler, - }, - { - MethodName: "UpdateProvider", - Handler: _OpenShell_UpdateProvider_Handler, - }, - { - MethodName: "GetProviderRefreshStatus", - Handler: _OpenShell_GetProviderRefreshStatus_Handler, - }, - { - MethodName: "ConfigureProviderRefresh", - Handler: _OpenShell_ConfigureProviderRefresh_Handler, - }, - { - MethodName: "RotateProviderCredential", - Handler: _OpenShell_RotateProviderCredential_Handler, - }, - { - MethodName: "DeleteProviderRefresh", - Handler: _OpenShell_DeleteProviderRefresh_Handler, - }, - { - MethodName: "DeleteProvider", - Handler: _OpenShell_DeleteProvider_Handler, - }, - { - MethodName: "DeleteProviderProfile", - Handler: _OpenShell_DeleteProviderProfile_Handler, - }, - { - MethodName: "GetSandboxConfig", - Handler: _OpenShell_GetSandboxConfig_Handler, - }, - { - MethodName: "GetGatewayConfig", - Handler: _OpenShell_GetGatewayConfig_Handler, - }, - { - MethodName: "UpdateConfig", - Handler: _OpenShell_UpdateConfig_Handler, - }, - { - MethodName: "GetSandboxPolicyStatus", - Handler: _OpenShell_GetSandboxPolicyStatus_Handler, - }, - { - MethodName: "ListSandboxPolicies", - Handler: _OpenShell_ListSandboxPolicies_Handler, - }, - { - MethodName: "ReportPolicyStatus", - Handler: _OpenShell_ReportPolicyStatus_Handler, - }, - { - MethodName: "ReportEndpointStatus", - Handler: _OpenShell_ReportEndpointStatus_Handler, - }, - { - MethodName: "ReportProviderReadiness", - Handler: _OpenShell_ReportProviderReadiness_Handler, - }, - { - MethodName: "ReportSandboxConfiguration", - Handler: _OpenShell_ReportSandboxConfiguration_Handler, - }, - { - MethodName: "GetSandboxProviderEnvironment", - Handler: _OpenShell_GetSandboxProviderEnvironment_Handler, - }, - { - MethodName: "ExchangeProviderSubjectToken", - Handler: _OpenShell_ExchangeProviderSubjectToken_Handler, - }, - { - MethodName: "GetSandboxLogs", - Handler: _OpenShell_GetSandboxLogs_Handler, - }, - { - MethodName: "ReportMainProcessExit", - Handler: _OpenShell_ReportMainProcessExit_Handler, - }, - { - MethodName: "FinalizeMainProcessExit", - Handler: _OpenShell_FinalizeMainProcessExit_Handler, - }, - { - MethodName: "PeerReportProviderReadiness", - Handler: _OpenShell_PeerReportProviderReadiness_Handler, - }, - { - MethodName: "PeerReportEndpointStatus", - Handler: _OpenShell_PeerReportEndpointStatus_Handler, - }, - { - MethodName: "PeerGetSandboxProviderStatus", - Handler: _OpenShell_PeerGetSandboxProviderStatus_Handler, - }, - { - MethodName: "PeerNotifyConfigUpdate", - Handler: _OpenShell_PeerNotifyConfigUpdate_Handler, - }, - { - MethodName: "SubmitPolicyAnalysis", - Handler: _OpenShell_SubmitPolicyAnalysis_Handler, - }, - { - MethodName: "GetDraftPolicy", - Handler: _OpenShell_GetDraftPolicy_Handler, - }, - { - MethodName: "ApproveDraftChunk", - Handler: _OpenShell_ApproveDraftChunk_Handler, - }, - { - MethodName: "RejectDraftChunk", - Handler: _OpenShell_RejectDraftChunk_Handler, - }, - { - MethodName: "ApproveAllDraftChunks", - Handler: _OpenShell_ApproveAllDraftChunks_Handler, - }, - { - MethodName: "EditDraftChunk", - Handler: _OpenShell_EditDraftChunk_Handler, - }, - { - MethodName: "UndoDraftChunk", - Handler: _OpenShell_UndoDraftChunk_Handler, - }, - { - MethodName: "ClearDraftChunks", - Handler: _OpenShell_ClearDraftChunks_Handler, - }, - { - MethodName: "GetDraftHistory", - Handler: _OpenShell_GetDraftHistory_Handler, - }, - { - MethodName: "IssueSandboxToken", - Handler: _OpenShell_IssueSandboxToken_Handler, - }, - { - MethodName: "RefreshSandboxToken", - Handler: _OpenShell_RefreshSandboxToken_Handler, - }, - { - MethodName: "CreateWorkspace", - Handler: _OpenShell_CreateWorkspace_Handler, - }, - { - MethodName: "GetWorkspace", - Handler: _OpenShell_GetWorkspace_Handler, - }, - { - MethodName: "ListWorkspaces", - Handler: _OpenShell_ListWorkspaces_Handler, - }, - { - MethodName: "DeleteWorkspace", - Handler: _OpenShell_DeleteWorkspace_Handler, - }, - { - MethodName: "AddWorkspaceMember", - Handler: _OpenShell_AddWorkspaceMember_Handler, - }, - { - MethodName: "RemoveWorkspaceMember", - Handler: _OpenShell_RemoveWorkspaceMember_Handler, - }, - { - MethodName: "ListWorkspaceMembers", - Handler: _OpenShell_ListWorkspaceMembers_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "ExecSandbox", - Handler: _OpenShell_ExecSandbox_Handler, - ServerStreams: true, - }, - { - StreamName: "ForwardTcp", - Handler: _OpenShell_ForwardTcp_Handler, - ServerStreams: true, - ClientStreams: true, - }, - { - StreamName: "ExecSandboxInteractive", - Handler: _OpenShell_ExecSandboxInteractive_Handler, - ServerStreams: true, - ClientStreams: true, - }, - { - StreamName: "PushSandboxLogs", - Handler: _OpenShell_PushSandboxLogs_Handler, - ClientStreams: true, - }, - { - StreamName: "ConnectSupervisor", - Handler: _OpenShell_ConnectSupervisor_Handler, - ServerStreams: true, - ClientStreams: true, - }, - { - StreamName: "RelayStream", - Handler: _OpenShell_RelayStream_Handler, - ServerStreams: true, - ClientStreams: true, - }, - { - StreamName: "PeerRelay", - Handler: _OpenShell_PeerRelay_Handler, - ServerStreams: true, - ClientStreams: true, - }, - { - StreamName: "WatchSandbox", - Handler: _OpenShell_WatchSandbox_Handler, - ServerStreams: true, - }, - }, - Metadata: "openshell.proto", -} - -const ( - SandboxUpdates_UpdateSandbox_FullMethodName = "/openshell.v1.SandboxUpdates/UpdateSandbox" -) - -// SandboxUpdatesClient is the client API for SandboxUpdates service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -// -// Optional sandbox mutations are separate from OpenShell so existing generated -// OpenShell server implementations remain source-compatible. -type SandboxUpdatesClient interface { - // Update the workload image, command, environment, labels, or annotations of a - // stopped sandbox while preserving its identity and retained storage. Requires - // compute-driver support for the selected changes and explicit gateway activation - // of sandbox updates. - UpdateSandbox(ctx context.Context, in *UpdateSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) -} - -type sandboxUpdatesClient struct { - cc grpc.ClientConnInterface -} - -func NewSandboxUpdatesClient(cc grpc.ClientConnInterface) SandboxUpdatesClient { - return &sandboxUpdatesClient{cc} -} - -func (c *sandboxUpdatesClient) UpdateSandbox(ctx context.Context, in *UpdateSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SandboxResponse) - err := c.cc.Invoke(ctx, SandboxUpdates_UpdateSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// SandboxUpdatesServer is the server API for SandboxUpdates service. -// All implementations must embed UnimplementedSandboxUpdatesServer -// for forward compatibility. -// -// Optional sandbox mutations are separate from OpenShell so existing generated -// OpenShell server implementations remain source-compatible. -type SandboxUpdatesServer interface { - // Update the workload image, command, environment, labels, or annotations of a - // stopped sandbox while preserving its identity and retained storage. Requires - // compute-driver support for the selected changes and explicit gateway activation - // of sandbox updates. - UpdateSandbox(context.Context, *UpdateSandboxRequest) (*SandboxResponse, error) - mustEmbedUnimplementedSandboxUpdatesServer() -} - -// UnimplementedSandboxUpdatesServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedSandboxUpdatesServer struct{} - -func (UnimplementedSandboxUpdatesServer) UpdateSandbox(context.Context, *UpdateSandboxRequest) (*SandboxResponse, error) { - return nil, status.Error(codes.Unimplemented, "method UpdateSandbox not implemented") -} -func (UnimplementedSandboxUpdatesServer) mustEmbedUnimplementedSandboxUpdatesServer() {} -func (UnimplementedSandboxUpdatesServer) testEmbeddedByValue() {} - -// UnsafeSandboxUpdatesServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to SandboxUpdatesServer will -// result in compilation errors. -type UnsafeSandboxUpdatesServer interface { - mustEmbedUnimplementedSandboxUpdatesServer() -} - -func RegisterSandboxUpdatesServer(s grpc.ServiceRegistrar, srv SandboxUpdatesServer) { - // If the following call panics, it indicates UnimplementedSandboxUpdatesServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } - s.RegisterService(&SandboxUpdates_ServiceDesc, srv) -} - -func _SandboxUpdates_UpdateSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SandboxUpdatesServer).UpdateSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: SandboxUpdates_UpdateSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SandboxUpdatesServer).UpdateSandbox(ctx, req.(*UpdateSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// SandboxUpdates_ServiceDesc is the grpc.ServiceDesc for SandboxUpdates service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var SandboxUpdates_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "openshell.v1.SandboxUpdates", - HandlerType: (*SandboxUpdatesServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "UpdateSandbox", - Handler: _SandboxUpdates_UpdateSandbox_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "openshell.proto", -} diff --git a/sdk/go/proto/optionsv1/options.pb.go b/sdk/go/proto/optionsv1/options.pb.go deleted file mode 100644 index 85b0903037..0000000000 --- a/sdk/go/proto/optionsv1/options.pb.go +++ /dev/null @@ -1,205 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: options.proto - -package optionsv1 - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - descriptorpb "google.golang.org/protobuf/types/descriptorpb" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Per-method authorization rule. Consumed at runtime by the gateway's -// descriptor-pool-based auth table to enforce auth mode, role, and scope. -type AuthorizationRule struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Authentication mode: "bearer", "sandbox", "dual", "peer", "operator", - // or "unauthenticated". Operator requires a verified mTLS operator certificate. - AuthMode string `protobuf:"bytes,1,opt,name=auth_mode,json=authMode,proto3" json:"auth_mode,omitempty"` - // Minimum workspace-level role required (checked by handler via - // authorize_workspace): "user" or "admin". Mutually exclusive with - // global_role. - WorkspaceRole string `protobuf:"bytes,2,opt,name=workspace_role,json=workspaceRole,proto3" json:"workspace_role,omitempty"` - // Global role required (checked by middleware via OIDC claims): - // "platform_admin". Mutually exclusive with workspace_role. - GlobalRole string `protobuf:"bytes,3,opt,name=global_role,json=globalRole,proto3" json:"global_role,omitempty"` - // Required OIDC scope on the bearer path (e.g. "sandbox:read"). - Scope string `protobuf:"bytes,4,opt,name=scope,proto3" json:"scope,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AuthorizationRule) Reset() { - *x = AuthorizationRule{} - mi := &file_options_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AuthorizationRule) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AuthorizationRule) ProtoMessage() {} - -func (x *AuthorizationRule) ProtoReflect() protoreflect.Message { - mi := &file_options_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AuthorizationRule.ProtoReflect.Descriptor instead. -func (*AuthorizationRule) Descriptor() ([]byte, []int) { - return file_options_proto_rawDescGZIP(), []int{0} -} - -func (x *AuthorizationRule) GetAuthMode() string { - if x != nil { - return x.AuthMode - } - return "" -} - -func (x *AuthorizationRule) GetWorkspaceRole() string { - if x != nil { - return x.WorkspaceRole - } - return "" -} - -func (x *AuthorizationRule) GetGlobalRole() string { - if x != nil { - return x.GlobalRole - } - return "" -} - -func (x *AuthorizationRule) GetScope() string { - if x != nil { - return x.Scope - } - return "" -} - -var file_options_proto_extTypes = []protoimpl.ExtensionInfo{ - { - ExtendedType: (*descriptorpb.MethodOptions)(nil), - ExtensionType: (*AuthorizationRule)(nil), - Field: 50000, - Name: "openshell.options.v1.authorization", - Tag: "bytes,50000,opt,name=authorization", - Filename: "options.proto", - }, - { - ExtendedType: (*descriptorpb.FieldOptions)(nil), - ExtensionType: (*bool)(nil), - Field: 50001, - Name: "openshell.options.v1.secret", - Tag: "varint,50001,opt,name=secret", - Filename: "options.proto", - }, -} - -// Extension fields to descriptorpb.MethodOptions. -var ( - // Authorization metadata for a gRPC method. - // - // optional openshell.options.v1.AuthorizationRule authorization = 50000; - E_Authorization = &file_options_proto_extTypes[0] -) - -// Extension fields to descriptorpb.FieldOptions. -var ( - // optional bool secret = 50001; - E_Secret = &file_options_proto_extTypes[1] -) - -var File_options_proto protoreflect.FileDescriptor - -const file_options_proto_rawDesc = "" + - "\n" + - "\roptions.proto\x12\x14openshell.options.v1\x1a google/protobuf/descriptor.proto\"\x8e\x01\n" + - "\x11AuthorizationRule\x12\x1b\n" + - "\tauth_mode\x18\x01 \x01(\tR\bauthMode\x12%\n" + - "\x0eworkspace_role\x18\x02 \x01(\tR\rworkspaceRole\x12\x1f\n" + - "\vglobal_role\x18\x03 \x01(\tR\n" + - "globalRole\x12\x14\n" + - "\x05scope\x18\x04 \x01(\tR\x05scope:o\n" + - "\rauthorization\x12\x1e.google.protobuf.MethodOptions\x18І\x03 \x01(\v2'.openshell.options.v1.AuthorizationRuleR\rauthorization:7\n" + - "\x06secret\x12\x1d.google.protobuf.FieldOptions\x18ц\x03 \x01(\bR\x06secretb\x06proto3" - -var ( - file_options_proto_rawDescOnce sync.Once - file_options_proto_rawDescData []byte -) - -func file_options_proto_rawDescGZIP() []byte { - file_options_proto_rawDescOnce.Do(func() { - file_options_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_options_proto_rawDesc), len(file_options_proto_rawDesc))) - }) - return file_options_proto_rawDescData -} - -var file_options_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_options_proto_goTypes = []any{ - (*AuthorizationRule)(nil), // 0: openshell.options.v1.AuthorizationRule - (*descriptorpb.MethodOptions)(nil), // 1: google.protobuf.MethodOptions - (*descriptorpb.FieldOptions)(nil), // 2: google.protobuf.FieldOptions -} -var file_options_proto_depIdxs = []int32{ - 1, // 0: openshell.options.v1.authorization:extendee -> google.protobuf.MethodOptions - 2, // 1: openshell.options.v1.secret:extendee -> google.protobuf.FieldOptions - 0, // 2: openshell.options.v1.authorization:type_name -> openshell.options.v1.AuthorizationRule - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 2, // [2:3] is the sub-list for extension type_name - 0, // [0:2] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_options_proto_init() } -func file_options_proto_init() { - if File_options_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_options_proto_rawDesc), len(file_options_proto_rawDesc)), - NumEnums: 0, - NumMessages: 1, - NumExtensions: 2, - NumServices: 0, - }, - GoTypes: file_options_proto_goTypes, - DependencyIndexes: file_options_proto_depIdxs, - MessageInfos: file_options_proto_msgTypes, - ExtensionInfos: file_options_proto_extTypes, - }.Build() - File_options_proto = out.File - file_options_proto_goTypes = nil - file_options_proto_depIdxs = nil -} diff --git a/sdk/go/proto/sandboxv1/sandbox.pb.go b/sdk/go/proto/sandboxv1/sandbox.pb.go deleted file mode 100644 index 045e15fac5..0000000000 --- a/sdk/go/proto/sandboxv1/sandbox.pb.go +++ /dev/null @@ -1,3101 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: sandbox.proto - -package sandboxv1 - -import ( - datamodelv1 "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - durationpb "google.golang.org/protobuf/types/known/durationpb" - structpb "google.golang.org/protobuf/types/known/structpb" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// TLS handling for a network endpoint. -type NetworkTlsMode int32 - -const ( - // Auto-detect TLS and terminate it when inspection is configured. - NetworkTlsMode_NETWORK_TLS_MODE_UNSPECIFIED NetworkTlsMode = 0 - // Disable TLS detection and relay the connection without inspection. - NetworkTlsMode_NETWORK_TLS_MODE_SKIP NetworkTlsMode = 1 - // Rejected by policy validation; use NETWORK_TLS_MODE_UNSPECIFIED. - // - // Deprecated: Marked as deprecated in sandbox.proto. - NetworkTlsMode_NETWORK_TLS_MODE_TERMINATE NetworkTlsMode = 2 - // Rejected by policy validation; use NETWORK_TLS_MODE_UNSPECIFIED. - // - // Deprecated: Marked as deprecated in sandbox.proto. - NetworkTlsMode_NETWORK_TLS_MODE_PASSTHROUGH NetworkTlsMode = 3 -) - -// Enum value maps for NetworkTlsMode. -var ( - NetworkTlsMode_name = map[int32]string{ - 0: "NETWORK_TLS_MODE_UNSPECIFIED", - 1: "NETWORK_TLS_MODE_SKIP", - 2: "NETWORK_TLS_MODE_TERMINATE", - 3: "NETWORK_TLS_MODE_PASSTHROUGH", - } - NetworkTlsMode_value = map[string]int32{ - "NETWORK_TLS_MODE_UNSPECIFIED": 0, - "NETWORK_TLS_MODE_SKIP": 1, - "NETWORK_TLS_MODE_TERMINATE": 2, - "NETWORK_TLS_MODE_PASSTHROUGH": 3, - } -) - -func (x NetworkTlsMode) Enum() *NetworkTlsMode { - p := new(NetworkTlsMode) - *p = x - return p -} - -func (x NetworkTlsMode) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (NetworkTlsMode) Descriptor() protoreflect.EnumDescriptor { - return file_sandbox_proto_enumTypes[0].Descriptor() -} - -func (NetworkTlsMode) Type() protoreflect.EnumType { - return &file_sandbox_proto_enumTypes[0] -} - -func (x NetworkTlsMode) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use NetworkTlsMode.Descriptor instead. -func (NetworkTlsMode) EnumDescriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{0} -} - -// Enforcement behavior for inspected network traffic. -type NetworkEnforcementMode int32 - -const ( - // Preserve the existing default: log violations and allow traffic. - NetworkEnforcementMode_NETWORK_ENFORCEMENT_MODE_UNSPECIFIED NetworkEnforcementMode = 0 - // Deny traffic that does not satisfy the endpoint policy. - NetworkEnforcementMode_NETWORK_ENFORCEMENT_MODE_ENFORCE NetworkEnforcementMode = 1 - // Log policy violations and allow the traffic. - NetworkEnforcementMode_NETWORK_ENFORCEMENT_MODE_AUDIT NetworkEnforcementMode = 2 -) - -// Enum value maps for NetworkEnforcementMode. -var ( - NetworkEnforcementMode_name = map[int32]string{ - 0: "NETWORK_ENFORCEMENT_MODE_UNSPECIFIED", - 1: "NETWORK_ENFORCEMENT_MODE_ENFORCE", - 2: "NETWORK_ENFORCEMENT_MODE_AUDIT", - } - NetworkEnforcementMode_value = map[string]int32{ - "NETWORK_ENFORCEMENT_MODE_UNSPECIFIED": 0, - "NETWORK_ENFORCEMENT_MODE_ENFORCE": 1, - "NETWORK_ENFORCEMENT_MODE_AUDIT": 2, - } -) - -func (x NetworkEnforcementMode) Enum() *NetworkEnforcementMode { - p := new(NetworkEnforcementMode) - *p = x - return p -} - -func (x NetworkEnforcementMode) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (NetworkEnforcementMode) Descriptor() protoreflect.EnumDescriptor { - return file_sandbox_proto_enumTypes[1].Descriptor() -} - -func (NetworkEnforcementMode) Type() protoreflect.EnumType { - return &file_sandbox_proto_enumTypes[1] -} - -func (x NetworkEnforcementMode) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use NetworkEnforcementMode.Descriptor instead. -func (NetworkEnforcementMode) EnumDescriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{1} -} - -// Shorthand access policy for an inspected network endpoint. -type NetworkAccessPreset int32 - -const ( - // No access preset; explicit rules may define allowed traffic. - NetworkAccessPreset_NETWORK_ACCESS_PRESET_UNSPECIFIED NetworkAccessPreset = 0 - // Allow read-only operations for the selected protocol. - NetworkAccessPreset_NETWORK_ACCESS_PRESET_READ_ONLY NetworkAccessPreset = 1 - // Allow read and write operations for the selected protocol. - NetworkAccessPreset_NETWORK_ACCESS_PRESET_READ_WRITE NetworkAccessPreset = 2 - // Allow every operation supported by the selected protocol. - NetworkAccessPreset_NETWORK_ACCESS_PRESET_FULL NetworkAccessPreset = 3 -) - -// Enum value maps for NetworkAccessPreset. -var ( - NetworkAccessPreset_name = map[int32]string{ - 0: "NETWORK_ACCESS_PRESET_UNSPECIFIED", - 1: "NETWORK_ACCESS_PRESET_READ_ONLY", - 2: "NETWORK_ACCESS_PRESET_READ_WRITE", - 3: "NETWORK_ACCESS_PRESET_FULL", - } - NetworkAccessPreset_value = map[string]int32{ - "NETWORK_ACCESS_PRESET_UNSPECIFIED": 0, - "NETWORK_ACCESS_PRESET_READ_ONLY": 1, - "NETWORK_ACCESS_PRESET_READ_WRITE": 2, - "NETWORK_ACCESS_PRESET_FULL": 3, - } -) - -func (x NetworkAccessPreset) Enum() *NetworkAccessPreset { - p := new(NetworkAccessPreset) - *p = x - return p -} - -func (x NetworkAccessPreset) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (NetworkAccessPreset) Descriptor() protoreflect.EnumDescriptor { - return file_sandbox_proto_enumTypes[2].Descriptor() -} - -func (NetworkAccessPreset) Type() protoreflect.EnumType { - return &file_sandbox_proto_enumTypes[2] -} - -func (x NetworkAccessPreset) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use NetworkAccessPreset.Descriptor instead. -func (NetworkAccessPreset) EnumDescriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{2} -} - -// Scope that currently controls a setting. -type SettingScope int32 - -const ( - SettingScope_SETTING_SCOPE_UNSPECIFIED SettingScope = 0 - SettingScope_SETTING_SCOPE_SANDBOX SettingScope = 1 - SettingScope_SETTING_SCOPE_GLOBAL SettingScope = 2 -) - -// Enum value maps for SettingScope. -var ( - SettingScope_name = map[int32]string{ - 0: "SETTING_SCOPE_UNSPECIFIED", - 1: "SETTING_SCOPE_SANDBOX", - 2: "SETTING_SCOPE_GLOBAL", - } - SettingScope_value = map[string]int32{ - "SETTING_SCOPE_UNSPECIFIED": 0, - "SETTING_SCOPE_SANDBOX": 1, - "SETTING_SCOPE_GLOBAL": 2, - } -) - -func (x SettingScope) Enum() *SettingScope { - p := new(SettingScope) - *p = x - return p -} - -func (x SettingScope) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (SettingScope) Descriptor() protoreflect.EnumDescriptor { - return file_sandbox_proto_enumTypes[3].Descriptor() -} - -func (SettingScope) Type() protoreflect.EnumType { - return &file_sandbox_proto_enumTypes[3] -} - -func (x SettingScope) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use SettingScope.Descriptor instead. -func (SettingScope) EnumDescriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{3} -} - -// Source used for a sandbox configuration payload. -type PolicySource int32 - -const ( - PolicySource_POLICY_SOURCE_UNSPECIFIED PolicySource = 0 - PolicySource_POLICY_SOURCE_SANDBOX PolicySource = 1 - PolicySource_POLICY_SOURCE_GLOBAL PolicySource = 2 -) - -// Enum value maps for PolicySource. -var ( - PolicySource_name = map[int32]string{ - 0: "POLICY_SOURCE_UNSPECIFIED", - 1: "POLICY_SOURCE_SANDBOX", - 2: "POLICY_SOURCE_GLOBAL", - } - PolicySource_value = map[string]int32{ - "POLICY_SOURCE_UNSPECIFIED": 0, - "POLICY_SOURCE_SANDBOX": 1, - "POLICY_SOURCE_GLOBAL": 2, - } -) - -func (x PolicySource) Enum() *PolicySource { - p := new(PolicySource) - *p = x - return p -} - -func (x PolicySource) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (PolicySource) Descriptor() protoreflect.EnumDescriptor { - return file_sandbox_proto_enumTypes[4].Descriptor() -} - -func (PolicySource) Type() protoreflect.EnumType { - return &file_sandbox_proto_enumTypes[4] -} - -func (x PolicySource) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use PolicySource.Descriptor instead. -func (PolicySource) EnumDescriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{4} -} - -// Operator authority over the bindings in a supervisor middleware manifest. -type SupervisorMiddlewareBindingPolicy int32 - -const ( - // Older gateways omit this field, which selects dynamic discovery. - SupervisorMiddlewareBindingPolicy_SUPERVISOR_MIDDLEWARE_BINDING_POLICY_UNSPECIFIED SupervisorMiddlewareBindingPolicy = 0 - // Accept all supported bindings in the manifest. - SupervisorMiddlewareBindingPolicy_SUPERVISOR_MIDDLEWARE_BINDING_POLICY_DYNAMIC SupervisorMiddlewareBindingPolicy = 1 - // Require and enable the listed bindings; ignore other supported bindings. - SupervisorMiddlewareBindingPolicy_SUPERVISOR_MIDDLEWARE_BINDING_POLICY_ALLOWLIST SupervisorMiddlewareBindingPolicy = 2 - // Require the listed bindings and reject additional bindings. - SupervisorMiddlewareBindingPolicy_SUPERVISOR_MIDDLEWARE_BINDING_POLICY_EXACT SupervisorMiddlewareBindingPolicy = 3 -) - -// Enum value maps for SupervisorMiddlewareBindingPolicy. -var ( - SupervisorMiddlewareBindingPolicy_name = map[int32]string{ - 0: "SUPERVISOR_MIDDLEWARE_BINDING_POLICY_UNSPECIFIED", - 1: "SUPERVISOR_MIDDLEWARE_BINDING_POLICY_DYNAMIC", - 2: "SUPERVISOR_MIDDLEWARE_BINDING_POLICY_ALLOWLIST", - 3: "SUPERVISOR_MIDDLEWARE_BINDING_POLICY_EXACT", - } - SupervisorMiddlewareBindingPolicy_value = map[string]int32{ - "SUPERVISOR_MIDDLEWARE_BINDING_POLICY_UNSPECIFIED": 0, - "SUPERVISOR_MIDDLEWARE_BINDING_POLICY_DYNAMIC": 1, - "SUPERVISOR_MIDDLEWARE_BINDING_POLICY_ALLOWLIST": 2, - "SUPERVISOR_MIDDLEWARE_BINDING_POLICY_EXACT": 3, - } -) - -func (x SupervisorMiddlewareBindingPolicy) Enum() *SupervisorMiddlewareBindingPolicy { - p := new(SupervisorMiddlewareBindingPolicy) - *p = x - return p -} - -func (x SupervisorMiddlewareBindingPolicy) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (SupervisorMiddlewareBindingPolicy) Descriptor() protoreflect.EnumDescriptor { - return file_sandbox_proto_enumTypes[5].Descriptor() -} - -func (SupervisorMiddlewareBindingPolicy) Type() protoreflect.EnumType { - return &file_sandbox_proto_enumTypes[5] -} - -func (x SupervisorMiddlewareBindingPolicy) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use SupervisorMiddlewareBindingPolicy.Descriptor instead. -func (SupervisorMiddlewareBindingPolicy) EnumDescriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{5} -} - -// An operation/phase pair that the platform can authorize. These identify -// platform capabilities, not service-owned binding names. -type SupervisorMiddlewareBinding int32 - -const ( - SupervisorMiddlewareBinding_SUPERVISOR_MIDDLEWARE_BINDING_UNSPECIFIED SupervisorMiddlewareBinding = 0 - SupervisorMiddlewareBinding_SUPERVISOR_MIDDLEWARE_BINDING_HTTP_REQUEST_PRE_CREDENTIALS SupervisorMiddlewareBinding = 1 - SupervisorMiddlewareBinding_SUPERVISOR_MIDDLEWARE_BINDING_HTTP_RESPONSE_PRE_RETURN SupervisorMiddlewareBinding = 2 - SupervisorMiddlewareBinding_SUPERVISOR_MIDDLEWARE_BINDING_WEBSOCKET_MESSAGE_PRE_CREDENTIALS SupervisorMiddlewareBinding = 3 -) - -// Enum value maps for SupervisorMiddlewareBinding. -var ( - SupervisorMiddlewareBinding_name = map[int32]string{ - 0: "SUPERVISOR_MIDDLEWARE_BINDING_UNSPECIFIED", - 1: "SUPERVISOR_MIDDLEWARE_BINDING_HTTP_REQUEST_PRE_CREDENTIALS", - 2: "SUPERVISOR_MIDDLEWARE_BINDING_HTTP_RESPONSE_PRE_RETURN", - 3: "SUPERVISOR_MIDDLEWARE_BINDING_WEBSOCKET_MESSAGE_PRE_CREDENTIALS", - } - SupervisorMiddlewareBinding_value = map[string]int32{ - "SUPERVISOR_MIDDLEWARE_BINDING_UNSPECIFIED": 0, - "SUPERVISOR_MIDDLEWARE_BINDING_HTTP_REQUEST_PRE_CREDENTIALS": 1, - "SUPERVISOR_MIDDLEWARE_BINDING_HTTP_RESPONSE_PRE_RETURN": 2, - "SUPERVISOR_MIDDLEWARE_BINDING_WEBSOCKET_MESSAGE_PRE_CREDENTIALS": 3, - } -) - -func (x SupervisorMiddlewareBinding) Enum() *SupervisorMiddlewareBinding { - p := new(SupervisorMiddlewareBinding) - *p = x - return p -} - -func (x SupervisorMiddlewareBinding) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (SupervisorMiddlewareBinding) Descriptor() protoreflect.EnumDescriptor { - return file_sandbox_proto_enumTypes[6].Descriptor() -} - -func (SupervisorMiddlewareBinding) Type() protoreflect.EnumType { - return &file_sandbox_proto_enumTypes[6] -} - -func (x SupervisorMiddlewareBinding) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use SupervisorMiddlewareBinding.Descriptor instead. -func (SupervisorMiddlewareBinding) EnumDescriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{6} -} - -// Sandbox security policy configuration. -type SandboxPolicy struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Policy version. - Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - // Filesystem access policy. - Filesystem *FilesystemPolicy `protobuf:"bytes,2,opt,name=filesystem,proto3" json:"filesystem,omitempty"` - // Landlock configuration. - Landlock *LandlockPolicy `protobuf:"bytes,3,opt,name=landlock,proto3" json:"landlock,omitempty"` - // Process execution policy. - Process *ProcessPolicy `protobuf:"bytes,4,opt,name=process,proto3" json:"process,omitempty"` - // Network access policies keyed by name (e.g. "claude_code", "gitlab"). - NetworkPolicies map[string]*NetworkPolicyRule `protobuf:"bytes,5,rep,name=network_policies,json=networkPolicies,proto3" json:"network_policies,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Reusable supervisor middleware configs for network egress, keyed by their - // policy-local names. At most 10 configs are accepted, and at most 10 stages - // can be selected per request. - NetworkMiddlewares map[string]*NetworkMiddlewareConfig `protobuf:"bytes,6,rep,name=network_middlewares,json=networkMiddlewares,proto3" json:"network_middlewares,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxPolicy) Reset() { - *x = SandboxPolicy{} - mi := &file_sandbox_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxPolicy) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxPolicy) ProtoMessage() {} - -func (x *SandboxPolicy) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxPolicy.ProtoReflect.Descriptor instead. -func (*SandboxPolicy) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{0} -} - -func (x *SandboxPolicy) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *SandboxPolicy) GetFilesystem() *FilesystemPolicy { - if x != nil { - return x.Filesystem - } - return nil -} - -func (x *SandboxPolicy) GetLandlock() *LandlockPolicy { - if x != nil { - return x.Landlock - } - return nil -} - -func (x *SandboxPolicy) GetProcess() *ProcessPolicy { - if x != nil { - return x.Process - } - return nil -} - -func (x *SandboxPolicy) GetNetworkPolicies() map[string]*NetworkPolicyRule { - if x != nil { - return x.NetworkPolicies - } - return nil -} - -func (x *SandboxPolicy) GetNetworkMiddlewares() map[string]*NetworkMiddlewareConfig { - if x != nil { - return x.NetworkMiddlewares - } - return nil -} - -// Filesystem access policy. -type FilesystemPolicy struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Automatically include the workdir as read-write. - IncludeWorkdir bool `protobuf:"varint,1,opt,name=include_workdir,json=includeWorkdir,proto3" json:"include_workdir,omitempty"` - // Read-only directory allow list. - ReadOnly []string `protobuf:"bytes,2,rep,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` - // Read-write directory allow list. - ReadWrite []string `protobuf:"bytes,3,rep,name=read_write,json=readWrite,proto3" json:"read_write,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *FilesystemPolicy) Reset() { - *x = FilesystemPolicy{} - mi := &file_sandbox_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *FilesystemPolicy) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FilesystemPolicy) ProtoMessage() {} - -func (x *FilesystemPolicy) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FilesystemPolicy.ProtoReflect.Descriptor instead. -func (*FilesystemPolicy) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{1} -} - -func (x *FilesystemPolicy) GetIncludeWorkdir() bool { - if x != nil { - return x.IncludeWorkdir - } - return false -} - -func (x *FilesystemPolicy) GetReadOnly() []string { - if x != nil { - return x.ReadOnly - } - return nil -} - -func (x *FilesystemPolicy) GetReadWrite() []string { - if x != nil { - return x.ReadWrite - } - return nil -} - -// Landlock policy configuration. -type LandlockPolicy struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Compatibility mode (e.g. "best_effort", "hard_requirement"). - Compatibility string `protobuf:"bytes,1,opt,name=compatibility,proto3" json:"compatibility,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *LandlockPolicy) Reset() { - *x = LandlockPolicy{} - mi := &file_sandbox_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LandlockPolicy) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LandlockPolicy) ProtoMessage() {} - -func (x *LandlockPolicy) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LandlockPolicy.ProtoReflect.Descriptor instead. -func (*LandlockPolicy) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{2} -} - -func (x *LandlockPolicy) GetCompatibility() string { - if x != nil { - return x.Compatibility - } - return "" -} - -// Process execution policy. -type ProcessPolicy struct { - state protoimpl.MessageState `protogen:"open.v1"` - // User name to run the sandboxed process as. - RunAsUser string `protobuf:"bytes,1,opt,name=run_as_user,json=runAsUser,proto3" json:"run_as_user,omitempty"` - // Group name to run the sandboxed process as. - RunAsGroup string `protobuf:"bytes,2,opt,name=run_as_group,json=runAsGroup,proto3" json:"run_as_group,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProcessPolicy) Reset() { - *x = ProcessPolicy{} - mi := &file_sandbox_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProcessPolicy) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProcessPolicy) ProtoMessage() {} - -func (x *ProcessPolicy) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProcessPolicy.ProtoReflect.Descriptor instead. -func (*ProcessPolicy) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{3} -} - -func (x *ProcessPolicy) GetRunAsUser() string { - if x != nil { - return x.RunAsUser - } - return "" -} - -func (x *ProcessPolicy) GetRunAsGroup() string { - if x != nil { - return x.RunAsGroup - } - return "" -} - -// A named network access policy rule. -type NetworkPolicyRule struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Human-readable name for this policy rule. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Allowed endpoint (host:port) pairs. - Endpoints []*NetworkEndpoint `protobuf:"bytes,2,rep,name=endpoints,proto3" json:"endpoints,omitempty"` - // Allowed binary identities. - Binaries []*NetworkBinary `protobuf:"bytes,3,rep,name=binaries,proto3" json:"binaries,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *NetworkPolicyRule) Reset() { - *x = NetworkPolicyRule{} - mi := &file_sandbox_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *NetworkPolicyRule) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NetworkPolicyRule) ProtoMessage() {} - -func (x *NetworkPolicyRule) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NetworkPolicyRule.ProtoReflect.Descriptor instead. -func (*NetworkPolicyRule) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{4} -} - -func (x *NetworkPolicyRule) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *NetworkPolicyRule) GetEndpoints() []*NetworkEndpoint { - if x != nil { - return x.Endpoints - } - return nil -} - -func (x *NetworkPolicyRule) GetBinaries() []*NetworkBinary { - if x != nil { - return x.Binaries - } - return nil -} - -// A reusable middleware config selected for admitted egress by host. -type NetworkMiddlewareConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Human-readable name for this middleware config. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Built-in middleware name or operator-owned registration name. - Middleware string `protobuf:"bytes,2,opt,name=middleware,proto3" json:"middleware,omitempty"` - // Service-specific configuration. - Config *structpb.Struct `protobuf:"bytes,3,opt,name=config,proto3" json:"config,omitempty"` - // Failure behavior: "fail_closed" (default) or "fail_open". - OnError string `protobuf:"bytes,4,opt,name=on_error,json=onError,proto3" json:"on_error,omitempty"` - // Host selector controlling which admitted destinations use this config. - Endpoints *MiddlewareEndpointSelector `protobuf:"bytes,5,opt,name=endpoints,proto3" json:"endpoints,omitempty"` - // Execution order. Values must be unique within a policy; lower values run first. - Order int32 `protobuf:"varint,6,opt,name=order,proto3" json:"order,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *NetworkMiddlewareConfig) Reset() { - *x = NetworkMiddlewareConfig{} - mi := &file_sandbox_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *NetworkMiddlewareConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NetworkMiddlewareConfig) ProtoMessage() {} - -func (x *NetworkMiddlewareConfig) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NetworkMiddlewareConfig.ProtoReflect.Descriptor instead. -func (*NetworkMiddlewareConfig) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{5} -} - -func (x *NetworkMiddlewareConfig) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *NetworkMiddlewareConfig) GetMiddleware() string { - if x != nil { - return x.Middleware - } - return "" -} - -func (x *NetworkMiddlewareConfig) GetConfig() *structpb.Struct { - if x != nil { - return x.Config - } - return nil -} - -func (x *NetworkMiddlewareConfig) GetOnError() string { - if x != nil { - return x.OnError - } - return "" -} - -func (x *NetworkMiddlewareConfig) GetEndpoints() *MiddlewareEndpointSelector { - if x != nil { - return x.Endpoints - } - return nil -} - -func (x *NetworkMiddlewareConfig) GetOrder() int32 { - if x != nil { - return x.Order - } - return 0 -} - -// Host selector controlling which admitted destinations use a middleware config. -type MiddlewareEndpointSelector struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Exact host or DNS glob patterns included in the selection. Include and - // exclude accept at most 32 combined patterns. - Include []string `protobuf:"bytes,1,rep,name=include,proto3" json:"include,omitempty"` - // Exact host or DNS glob patterns removed from the selection. - // Exclusions take precedence over inclusions. - Exclude []string `protobuf:"bytes,2,rep,name=exclude,proto3" json:"exclude,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *MiddlewareEndpointSelector) Reset() { - *x = MiddlewareEndpointSelector{} - mi := &file_sandbox_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *MiddlewareEndpointSelector) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MiddlewareEndpointSelector) ProtoMessage() {} - -func (x *MiddlewareEndpointSelector) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[6] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use MiddlewareEndpointSelector.ProtoReflect.Descriptor instead. -func (*MiddlewareEndpointSelector) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{6} -} - -func (x *MiddlewareEndpointSelector) GetInclude() []string { - if x != nil { - return x.Include - } - return nil -} - -func (x *MiddlewareEndpointSelector) GetExclude() []string { - if x != nil { - return x.Exclude - } - return nil -} - -// Binds a policy endpoint to static credentials from a sandbox provider. -type NetworkCredentialBinding struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Name of the provider attached to this sandbox whose static credentials - // may be resolved for requests admitted by this endpoint. - Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *NetworkCredentialBinding) Reset() { - *x = NetworkCredentialBinding{} - mi := &file_sandbox_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *NetworkCredentialBinding) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NetworkCredentialBinding) ProtoMessage() {} - -func (x *NetworkCredentialBinding) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[7] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NetworkCredentialBinding.ProtoReflect.Descriptor instead. -func (*NetworkCredentialBinding) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{7} -} - -func (x *NetworkCredentialBinding) GetProvider() string { - if x != nil { - return x.Provider - } - return "" -} - -// A network endpoint (host + port) with optional L7 inspection config. -type NetworkEndpoint struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Hostname or host glob pattern. Exact match is case-insensitive. - // Glob patterns use "." as delimiter: "*.example.com" matches a single - // subdomain label, "**.example.com" matches across labels. - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - // Single port (backwards compat). Use `ports` for multiple ports. - // Mutually exclusive with `ports` — if both are set, `ports` takes precedence. - Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` - // Endpoint protocol. "tcp" and "" select L4-only handling; "rest", - // "websocket", "graphql", "sql", "json-rpc", and "mcp" select L7 inspection. - Protocol string `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"` - // TLS handling. Unspecified enables automatic detection and termination. - Tls NetworkTlsMode `protobuf:"varint,4,opt,name=tls,proto3,enum=openshell.sandbox.v1.NetworkTlsMode" json:"tls,omitempty"` - // Enforcement mode. Unspecified preserves the audit default. - Enforcement NetworkEnforcementMode `protobuf:"varint,5,opt,name=enforcement,proto3,enum=openshell.sandbox.v1.NetworkEnforcementMode" json:"enforcement,omitempty"` - // Access preset shorthand. Unspecified means no preset. - // Mutually exclusive with rules. - Access NetworkAccessPreset `protobuf:"varint,6,opt,name=access,proto3,enum=openshell.sandbox.v1.NetworkAccessPreset" json:"access,omitempty"` - // Explicit L7 rules (mutually exclusive with access). - Rules []*L7Rule `protobuf:"bytes,7,rep,name=rules,proto3" json:"rules,omitempty"` - // Allowed resolved IP addresses or CIDR ranges for this endpoint. - // When non-empty, the SSRF internal-IP check is replaced by an allowlist check: - // - If host is also set: domain must resolve to an IP in this list. - // - If host is empty: any domain is allowed as long as it resolves to an IP in this list. - // - // Supports exact IPs ("10.0.5.20") and CIDR notation ("10.0.5.0/24"). - // Loopback (127.0.0.0/8) and link-local (169.254.0.0/16) are always blocked - // regardless of this field. - AllowedIps []string `protobuf:"bytes,8,rep,name=allowed_ips,json=allowedIps,proto3" json:"allowed_ips,omitempty"` - // Multiple ports. When non-empty, this endpoint covers all listed ports. - // If `port` is set and `ports` is empty, `port` is normalized to `ports: [port]`. - // If both are set, `ports` takes precedence. - Ports []uint32 `protobuf:"varint,9,rep,packed,name=ports,proto3" json:"ports,omitempty"` - // Explicit L7 deny rules. When present, requests matching any deny rule - // are blocked even if they match an allow rule or access preset. - // Deny rules take precedence over allow rules. - DenyRules []*L7DenyRule `protobuf:"bytes,10,rep,name=deny_rules,json=denyRules,proto3" json:"deny_rules,omitempty"` - // When true, percent-encoded '/' (%2F) is preserved in path segments - // rather than rejected by the L7 path canonicalizer. Required for - // upstreams like GitLab that embed %2F in namespaced resource paths. - // Defaults to false (strict). - AllowEncodedSlash bool `protobuf:"varint,11,opt,name=allow_encoded_slash,json=allowEncodedSlash,proto3" json:"allow_encoded_slash,omitempty"` - // GraphQL persisted-query behavior for hash-only/saved-query requests: - // "deny" (default) or "allow_registered". - PersistedQueries string `protobuf:"bytes,12,opt,name=persisted_queries,json=persistedQueries,proto3" json:"persisted_queries,omitempty"` - // Trusted GraphQL persisted-query registry keyed by hash or service-specific ID. - // Only used when persisted_queries is "allow_registered". - GraphqlPersistedQueries map[string]*GraphqlOperation `protobuf:"bytes,13,rep,name=graphql_persisted_queries,json=graphqlPersistedQueries,proto3" json:"graphql_persisted_queries,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Maximum GraphQL request body bytes to buffer for inspection. - // Defaults to 65536 when unset. - GraphqlMaxBodyBytes uint32 `protobuf:"varint,14,opt,name=graphql_max_body_bytes,json=graphqlMaxBodyBytes,proto3" json:"graphql_max_body_bytes,omitempty"` - // Optional HTTP path glob that scopes this L7 endpoint on shared host:port APIs. - // Example: use path "/graphql" for protocol "graphql" and "/repos/**" for - // protocol "rest" when both surfaces live under api.example.com:443. - // Empty means all paths. - Path string `protobuf:"bytes,15,opt,name=path,proto3" json:"path,omitempty"` - // When true on a "rest" endpoint, OpenShell rewrites credential placeholders - // inside client-to-server WebSocket text messages after an allowed HTTP 101 - // upgrade. Defaults to false. - WebsocketCredentialRewrite bool `protobuf:"varint,16,opt,name=websocket_credential_rewrite,json=websocketCredentialRewrite,proto3" json:"websocket_credential_rewrite,omitempty"` - // When true on a "rest" endpoint, OpenShell rewrites credential placeholders - // inside supported textual HTTP request bodies before forwarding upstream. - // Defaults to false. - RequestBodyCredentialRewrite bool `protobuf:"varint,17,opt,name=request_body_credential_rewrite,json=requestBodyCredentialRewrite,proto3" json:"request_body_credential_rewrite,omitempty"` - // Internal provenance marker for policy-advisor generated endpoints. - // Advisor-proposed endpoints must not satisfy exact-host SSRF trust unless - // they are converted through an explicit user-authored policy path. - AdvisorProposed bool `protobuf:"varint,18,opt,name=advisor_proposed,json=advisorProposed,proto3" json:"advisor_proposed,omitempty"` - // Proxy-side credential signing mode: "sigv4" for AWS SigV4 re-signing. - // When set, the proxy strips the client's Authorization header and computes - // a fresh SigV4 signature using real credentials from the provider. - CredentialSigning string `protobuf:"bytes,19,opt,name=credential_signing,json=credentialSigning,proto3" json:"credential_signing,omitempty"` - // AWS signing service name override. Required when credential_signing is - // "sigv4" — e.g. "bedrock" for bedrock-runtime endpoints. - SigningService string `protobuf:"bytes,20,opt,name=signing_service,json=signingService,proto3" json:"signing_service,omitempty"` - // AWS region override for SigV4 signing. When set, takes precedence over - // hostname-based region extraction. Required for non-standard endpoints. - SigningRegion string `protobuf:"bytes,21,opt,name=signing_region,json=signingRegion,proto3" json:"signing_region,omitempty"` - // Maximum JSON-RPC-over-HTTP request body bytes to buffer for inspection. - // Defaults to 65536 when unset. - JsonRpcMaxBodyBytes uint32 `protobuf:"varint,22,opt,name=json_rpc_max_body_bytes,json=jsonRpcMaxBodyBytes,proto3" json:"json_rpc_max_body_bytes,omitempty"` - // MCP-only policy and inspection options. Only used when protocol is "mcp". - Mcp *McpOptions `protobuf:"bytes,23,opt,name=mcp,proto3" json:"mcp,omitempty"` - // Explicit binding authority for static credentials from an attached - // endpointless provider profile. Profiles that already define endpoints - // continue to use those profile endpoints as their credential boundary. - CredentialBinding *NetworkCredentialBinding `protobuf:"bytes,24,opt,name=credential_binding,json=credentialBinding,proto3" json:"credential_binding,omitempty"` - // Explicitly permits credential-bearing traffic to use paths that OpenShell - // cannot inspect or rewrite. Defaults to false. This is a security-sensitive - // escape hatch and must be explicitly approved. - AllowUninspectedCredentials bool `protobuf:"varint,25,opt,name=allow_uninspected_credentials,json=allowUninspectedCredentials,proto3" json:"allow_uninspected_credentials,omitempty"` - // Internal gateway-derived marker indicating that this endpoint belongs to - // an attached credentialed provider. User-authored values are ignored. - ProviderCredentialed bool `protobuf:"varint,26,opt,name=provider_credentialed,json=providerCredentialed,proto3" json:"provider_credentialed,omitempty"` - // Internal gateway-derived identity of this endpoint's token-grant authority. - // It binds the provider record and complete profile policy, or the active - // global policy, to this endpoint. User-authored values are ignored. - TokenGrantOwner string `protobuf:"bytes,27,opt,name=token_grant_owner,json=tokenGrantOwner,proto3" json:"token_grant_owner,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *NetworkEndpoint) Reset() { - *x = NetworkEndpoint{} - mi := &file_sandbox_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *NetworkEndpoint) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NetworkEndpoint) ProtoMessage() {} - -func (x *NetworkEndpoint) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[8] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NetworkEndpoint.ProtoReflect.Descriptor instead. -func (*NetworkEndpoint) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{8} -} - -func (x *NetworkEndpoint) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *NetworkEndpoint) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - -func (x *NetworkEndpoint) GetProtocol() string { - if x != nil { - return x.Protocol - } - return "" -} - -func (x *NetworkEndpoint) GetTls() NetworkTlsMode { - if x != nil { - return x.Tls - } - return NetworkTlsMode_NETWORK_TLS_MODE_UNSPECIFIED -} - -func (x *NetworkEndpoint) GetEnforcement() NetworkEnforcementMode { - if x != nil { - return x.Enforcement - } - return NetworkEnforcementMode_NETWORK_ENFORCEMENT_MODE_UNSPECIFIED -} - -func (x *NetworkEndpoint) GetAccess() NetworkAccessPreset { - if x != nil { - return x.Access - } - return NetworkAccessPreset_NETWORK_ACCESS_PRESET_UNSPECIFIED -} - -func (x *NetworkEndpoint) GetRules() []*L7Rule { - if x != nil { - return x.Rules - } - return nil -} - -func (x *NetworkEndpoint) GetAllowedIps() []string { - if x != nil { - return x.AllowedIps - } - return nil -} - -func (x *NetworkEndpoint) GetPorts() []uint32 { - if x != nil { - return x.Ports - } - return nil -} - -func (x *NetworkEndpoint) GetDenyRules() []*L7DenyRule { - if x != nil { - return x.DenyRules - } - return nil -} - -func (x *NetworkEndpoint) GetAllowEncodedSlash() bool { - if x != nil { - return x.AllowEncodedSlash - } - return false -} - -func (x *NetworkEndpoint) GetPersistedQueries() string { - if x != nil { - return x.PersistedQueries - } - return "" -} - -func (x *NetworkEndpoint) GetGraphqlPersistedQueries() map[string]*GraphqlOperation { - if x != nil { - return x.GraphqlPersistedQueries - } - return nil -} - -func (x *NetworkEndpoint) GetGraphqlMaxBodyBytes() uint32 { - if x != nil { - return x.GraphqlMaxBodyBytes - } - return 0 -} - -func (x *NetworkEndpoint) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *NetworkEndpoint) GetWebsocketCredentialRewrite() bool { - if x != nil { - return x.WebsocketCredentialRewrite - } - return false -} - -func (x *NetworkEndpoint) GetRequestBodyCredentialRewrite() bool { - if x != nil { - return x.RequestBodyCredentialRewrite - } - return false -} - -func (x *NetworkEndpoint) GetAdvisorProposed() bool { - if x != nil { - return x.AdvisorProposed - } - return false -} - -func (x *NetworkEndpoint) GetCredentialSigning() string { - if x != nil { - return x.CredentialSigning - } - return "" -} - -func (x *NetworkEndpoint) GetSigningService() string { - if x != nil { - return x.SigningService - } - return "" -} - -func (x *NetworkEndpoint) GetSigningRegion() string { - if x != nil { - return x.SigningRegion - } - return "" -} - -func (x *NetworkEndpoint) GetJsonRpcMaxBodyBytes() uint32 { - if x != nil { - return x.JsonRpcMaxBodyBytes - } - return 0 -} - -func (x *NetworkEndpoint) GetMcp() *McpOptions { - if x != nil { - return x.Mcp - } - return nil -} - -func (x *NetworkEndpoint) GetCredentialBinding() *NetworkCredentialBinding { - if x != nil { - return x.CredentialBinding - } - return nil -} - -func (x *NetworkEndpoint) GetAllowUninspectedCredentials() bool { - if x != nil { - return x.AllowUninspectedCredentials - } - return false -} - -func (x *NetworkEndpoint) GetProviderCredentialed() bool { - if x != nil { - return x.ProviderCredentialed - } - return false -} - -func (x *NetworkEndpoint) GetTokenGrantOwner() string { - if x != nil { - return x.TokenGrantOwner - } - return "" -} - -// MCP options are grouped so MCP-specific policy can grow without adding more -// top-level NetworkEndpoint fields. OpenShell owns the supported revision -// profiles instead of treating dependency enums as the policy contract. -// -// Sources: -// - https://modelcontextprotocol.io/specification/2025-03-26/basic/transports -// - https://modelcontextprotocol.io/specification/2025-06-18/basic/transports -// - https://modelcontextprotocol.io/specification/2025-11-25/basic/transports -type McpOptions struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Hardening boundary for tools/call params.name. When unset or true, the - // supervisor enforces the MCP recommended tool-name syntax - // ^[A-Za-z0-9_.-]{1,128}$ before policy evaluation. Set false only for - // compatibility with servers that intentionally use non-recommended names. - // - // Source: - // - https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool-names - StrictToolNames *bool `protobuf:"varint,1,opt,name=strict_tool_names,json=strictToolNames,proto3,oneof" json:"strict_tool_names,omitempty"` - // Method-layer default for MCP endpoints. When true, OpenShell allows parsed - // MCP-family methods at the method layer unless a tool-name policy narrows - // tools/call. When unset or false, explicit method rules are required. - AllowAllKnownMcpMethods *bool `protobuf:"varint,2,opt,name=allow_all_known_mcp_methods,json=allowAllKnownMcpMethods,proto3,oneof" json:"allow_all_known_mcp_methods,omitempty"` - // Exact MCP protocol revisions accepted by the endpoint's policy schema. - // Authors may omit this field. Because proto3 repeated fields do not retain - // presence, omission reaches protobuf ingress as an empty list. Checked - // normalization materializes OpenShell's pinned default revision - // "2025-11-25" before producing canonical downstream state, which is always - // nonempty. - // - // An explicit nonempty list remains an exact allowlist. Duplicates, moving - // aliases such as "draft" and "latest", and unknown dates are invalid. - // Normalization stores valid values in canonical semantic order. These - // values identify core revisions; they do not enable separate SEP overlays - // or select runtime parsing and forwarding behavior. - Versions []string `protobuf:"bytes,3,rep,name=versions,proto3" json:"versions,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *McpOptions) Reset() { - *x = McpOptions{} - mi := &file_sandbox_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *McpOptions) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*McpOptions) ProtoMessage() {} - -func (x *McpOptions) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[9] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use McpOptions.ProtoReflect.Descriptor instead. -func (*McpOptions) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{9} -} - -func (x *McpOptions) GetStrictToolNames() bool { - if x != nil && x.StrictToolNames != nil { - return *x.StrictToolNames - } - return false -} - -func (x *McpOptions) GetAllowAllKnownMcpMethods() bool { - if x != nil && x.AllowAllKnownMcpMethods != nil { - return *x.AllowAllKnownMcpMethods - } - return false -} - -func (x *McpOptions) GetVersions() []string { - if x != nil { - return x.Versions - } - return nil -} - -// Trusted GraphQL operation classification. -type GraphqlOperation struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Operation type: "query", "mutation", or "subscription". - OperationType string `protobuf:"bytes,1,opt,name=operation_type,json=operationType,proto3" json:"operation_type,omitempty"` - // Operation name, if known. - OperationName string `protobuf:"bytes,2,opt,name=operation_name,json=operationName,proto3" json:"operation_name,omitempty"` - // Root field names selected by the operation. - Fields []string `protobuf:"bytes,3,rep,name=fields,proto3" json:"fields,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GraphqlOperation) Reset() { - *x = GraphqlOperation{} - mi := &file_sandbox_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GraphqlOperation) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GraphqlOperation) ProtoMessage() {} - -func (x *GraphqlOperation) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[10] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GraphqlOperation.ProtoReflect.Descriptor instead. -func (*GraphqlOperation) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{10} -} - -func (x *GraphqlOperation) GetOperationType() string { - if x != nil { - return x.OperationType - } - return "" -} - -func (x *GraphqlOperation) GetOperationName() string { - if x != nil { - return x.OperationName - } - return "" -} - -func (x *GraphqlOperation) GetFields() []string { - if x != nil { - return x.Fields - } - return nil -} - -// An L7 deny rule that blocks specific requests. -// Mirrors L7Allow — same fields, same matching semantics, inverted effect. -// Deny rules are evaluated after allow rules and take precedence. -type L7DenyRule struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Protocol method: HTTP method (REST/WebSocket), JSON-RPC method name, or - // "*" for any when supported by the protocol. - Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` - // URL path glob pattern (REST): "/repos/*/pulls/*/reviews", "**" for any. - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` - // SQL command (SQL): SELECT, INSERT, etc. or "*" for any. - Command string `protobuf:"bytes,3,opt,name=command,proto3" json:"command,omitempty"` - // Query parameter matcher map (REST). - // Same semantics as L7Allow.query. - Query map[string]*L7QueryMatcher `protobuf:"bytes,4,rep,name=query,proto3" json:"query,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // GraphQL operation type: "query", "mutation", "subscription", or "*" for any. - OperationType string `protobuf:"bytes,5,opt,name=operation_type,json=operationType,proto3" json:"operation_type,omitempty"` - // GraphQL operation name glob. "*" matches any operation name. - OperationName string `protobuf:"bytes,6,opt,name=operation_name,json=operationName,proto3" json:"operation_name,omitempty"` - // GraphQL root field globs. Deny rules match when any selected root field - // matches any configured glob. - Fields []string `protobuf:"bytes,7,rep,name=fields,proto3" json:"fields,omitempty"` - // MCP params matcher map. Currently only params.name is supported for - // tools/call filtering. Generic protocol "json-rpc" rejects params matchers. - Params map[string]*L7QueryMatcher `protobuf:"bytes,9,rep,name=params,proto3" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *L7DenyRule) Reset() { - *x = L7DenyRule{} - mi := &file_sandbox_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *L7DenyRule) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L7DenyRule) ProtoMessage() {} - -func (x *L7DenyRule) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[11] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L7DenyRule.ProtoReflect.Descriptor instead. -func (*L7DenyRule) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{11} -} - -func (x *L7DenyRule) GetMethod() string { - if x != nil { - return x.Method - } - return "" -} - -func (x *L7DenyRule) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *L7DenyRule) GetCommand() string { - if x != nil { - return x.Command - } - return "" -} - -func (x *L7DenyRule) GetQuery() map[string]*L7QueryMatcher { - if x != nil { - return x.Query - } - return nil -} - -func (x *L7DenyRule) GetOperationType() string { - if x != nil { - return x.OperationType - } - return "" -} - -func (x *L7DenyRule) GetOperationName() string { - if x != nil { - return x.OperationName - } - return "" -} - -func (x *L7DenyRule) GetFields() []string { - if x != nil { - return x.Fields - } - return nil -} - -func (x *L7DenyRule) GetParams() map[string]*L7QueryMatcher { - if x != nil { - return x.Params - } - return nil -} - -// An L7 policy rule (allow-only). -type L7Rule struct { - state protoimpl.MessageState `protogen:"open.v1"` - Allow *L7Allow `protobuf:"bytes,1,opt,name=allow,proto3" json:"allow,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *L7Rule) Reset() { - *x = L7Rule{} - mi := &file_sandbox_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *L7Rule) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L7Rule) ProtoMessage() {} - -func (x *L7Rule) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[12] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L7Rule.ProtoReflect.Descriptor instead. -func (*L7Rule) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{12} -} - -func (x *L7Rule) GetAllow() *L7Allow { - if x != nil { - return x.Allow - } - return nil -} - -// Allowed action definition for L7 rules. -type L7Allow struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Protocol method: HTTP method (REST/WebSocket), JSON-RPC method name, or - // "*" for any when supported by the protocol. - Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` - // URL path glob pattern (REST): "/repos/**", "**" for any. - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` - // SQL command (SQL): SELECT, INSERT, etc. or "*" for any. - Command string `protobuf:"bytes,3,opt,name=command,proto3" json:"command,omitempty"` - // Query parameter matcher map (REST). - // Key is the decoded query parameter name (case-sensitive). - // Value supports either a single glob (`glob`) or a list (`any`). - Query map[string]*L7QueryMatcher `protobuf:"bytes,4,rep,name=query,proto3" json:"query,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // GraphQL operation type: "query", "mutation", "subscription", or "*" for any. - OperationType string `protobuf:"bytes,5,opt,name=operation_type,json=operationType,proto3" json:"operation_type,omitempty"` - // GraphQL operation name glob. "*" matches any operation name. - OperationName string `protobuf:"bytes,6,opt,name=operation_name,json=operationName,proto3" json:"operation_name,omitempty"` - // GraphQL root field globs. Allow rules match only when every selected root - // field matches one of the configured globs. Omit to match all fields. - Fields []string `protobuf:"bytes,7,rep,name=fields,proto3" json:"fields,omitempty"` - // MCP params matcher map. Currently only params.name is supported for - // tools/call filtering. Generic protocol "json-rpc" rejects params matchers. - Params map[string]*L7QueryMatcher `protobuf:"bytes,9,rep,name=params,proto3" json:"params,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *L7Allow) Reset() { - *x = L7Allow{} - mi := &file_sandbox_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *L7Allow) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L7Allow) ProtoMessage() {} - -func (x *L7Allow) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[13] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L7Allow.ProtoReflect.Descriptor instead. -func (*L7Allow) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{13} -} - -func (x *L7Allow) GetMethod() string { - if x != nil { - return x.Method - } - return "" -} - -func (x *L7Allow) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *L7Allow) GetCommand() string { - if x != nil { - return x.Command - } - return "" -} - -func (x *L7Allow) GetQuery() map[string]*L7QueryMatcher { - if x != nil { - return x.Query - } - return nil -} - -func (x *L7Allow) GetOperationType() string { - if x != nil { - return x.OperationType - } - return "" -} - -func (x *L7Allow) GetOperationName() string { - if x != nil { - return x.OperationName - } - return "" -} - -func (x *L7Allow) GetFields() []string { - if x != nil { - return x.Fields - } - return nil -} - -func (x *L7Allow) GetParams() map[string]*L7QueryMatcher { - if x != nil { - return x.Params - } - return nil -} - -// Query value matcher for one query parameter key. -type L7QueryMatcher struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Single glob pattern. - Glob string `protobuf:"bytes,1,opt,name=glob,proto3" json:"glob,omitempty"` - // Any-of glob patterns. - Any []string `protobuf:"bytes,2,rep,name=any,proto3" json:"any,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *L7QueryMatcher) Reset() { - *x = L7QueryMatcher{} - mi := &file_sandbox_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *L7QueryMatcher) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L7QueryMatcher) ProtoMessage() {} - -func (x *L7QueryMatcher) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[14] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L7QueryMatcher.ProtoReflect.Descriptor instead. -func (*L7QueryMatcher) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{14} -} - -func (x *L7QueryMatcher) GetGlob() string { - if x != nil { - return x.Glob - } - return "" -} - -func (x *L7QueryMatcher) GetAny() []string { - if x != nil { - return x.Any - } - return nil -} - -// A binary identity for network policy matching. -type NetworkBinary struct { - state protoimpl.MessageState `protogen:"open.v1"` - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *NetworkBinary) Reset() { - *x = NetworkBinary{} - mi := &file_sandbox_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *NetworkBinary) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NetworkBinary) ProtoMessage() {} - -func (x *NetworkBinary) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[15] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NetworkBinary.ProtoReflect.Descriptor instead. -func (*NetworkBinary) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{15} -} - -func (x *NetworkBinary) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -// Request to get sandbox settings by sandbox name. -type GetSandboxConfigRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Workspace scope. Select one named workspace; authenticated sandbox callers may omit it during bootstrap. - WorkspaceScope *datamodelv1.WorkspaceSelector `protobuf:"bytes,3,opt,name=workspace_scope,json=workspaceScope,proto3" json:"workspace_scope,omitempty"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxConfigRequest) Reset() { - *x = GetSandboxConfigRequest{} - mi := &file_sandbox_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxConfigRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxConfigRequest) ProtoMessage() {} - -func (x *GetSandboxConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[16] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxConfigRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxConfigRequest) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{16} -} - -func (x *GetSandboxConfigRequest) GetWorkspaceScope() *datamodelv1.WorkspaceSelector { - if x != nil { - return x.WorkspaceScope - } - return nil -} - -func (x *GetSandboxConfigRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// Request to get gateway-global settings. -type GetGatewayConfigRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetGatewayConfigRequest) Reset() { - *x = GetGatewayConfigRequest{} - mi := &file_sandbox_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetGatewayConfigRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetGatewayConfigRequest) ProtoMessage() {} - -func (x *GetGatewayConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[17] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetGatewayConfigRequest.ProtoReflect.Descriptor instead. -func (*GetGatewayConfigRequest) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{17} -} - -// Response containing gateway-global settings. -type GetGatewayConfigResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Gateway-global settings map excluding the reserved policy key. - // Registered keys without a configured value are returned with an empty SettingValue. - Settings map[string]*SettingValue `protobuf:"bytes,1,rep,name=settings,proto3" json:"settings,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Monotonically increasing revision for gateway-global settings. - SettingsRevision uint64 `protobuf:"varint,2,opt,name=settings_revision,json=settingsRevision,proto3" json:"settings_revision,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetGatewayConfigResponse) Reset() { - *x = GetGatewayConfigResponse{} - mi := &file_sandbox_proto_msgTypes[18] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetGatewayConfigResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetGatewayConfigResponse) ProtoMessage() {} - -func (x *GetGatewayConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[18] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetGatewayConfigResponse.ProtoReflect.Descriptor instead. -func (*GetGatewayConfigResponse) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{18} -} - -func (x *GetGatewayConfigResponse) GetSettings() map[string]*SettingValue { - if x != nil { - return x.Settings - } - return nil -} - -func (x *GetGatewayConfigResponse) GetSettingsRevision() uint64 { - if x != nil { - return x.SettingsRevision - } - return 0 -} - -// Type-aware setting value for sandbox/gateway settings. -type SettingValue struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Value: - // - // *SettingValue_StringValue - // *SettingValue_BoolValue - // *SettingValue_IntValue - // *SettingValue_BytesValue - Value isSettingValue_Value `protobuf_oneof:"value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SettingValue) Reset() { - *x = SettingValue{} - mi := &file_sandbox_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SettingValue) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SettingValue) ProtoMessage() {} - -func (x *SettingValue) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[19] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SettingValue.ProtoReflect.Descriptor instead. -func (*SettingValue) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{19} -} - -func (x *SettingValue) GetValue() isSettingValue_Value { - if x != nil { - return x.Value - } - return nil -} - -func (x *SettingValue) GetStringValue() string { - if x != nil { - if x, ok := x.Value.(*SettingValue_StringValue); ok { - return x.StringValue - } - } - return "" -} - -func (x *SettingValue) GetBoolValue() bool { - if x != nil { - if x, ok := x.Value.(*SettingValue_BoolValue); ok { - return x.BoolValue - } - } - return false -} - -func (x *SettingValue) GetIntValue() int64 { - if x != nil { - if x, ok := x.Value.(*SettingValue_IntValue); ok { - return x.IntValue - } - } - return 0 -} - -func (x *SettingValue) GetBytesValue() []byte { - if x != nil { - if x, ok := x.Value.(*SettingValue_BytesValue); ok { - return x.BytesValue - } - } - return nil -} - -type isSettingValue_Value interface { - isSettingValue_Value() -} - -type SettingValue_StringValue struct { - StringValue string `protobuf:"bytes,1,opt,name=string_value,json=stringValue,proto3,oneof"` -} - -type SettingValue_BoolValue struct { - BoolValue bool `protobuf:"varint,2,opt,name=bool_value,json=boolValue,proto3,oneof"` -} - -type SettingValue_IntValue struct { - IntValue int64 `protobuf:"varint,3,opt,name=int_value,json=intValue,proto3,oneof"` -} - -type SettingValue_BytesValue struct { - BytesValue []byte `protobuf:"bytes,4,opt,name=bytes_value,json=bytesValue,proto3,oneof"` -} - -func (*SettingValue_StringValue) isSettingValue_Value() {} - -func (*SettingValue_BoolValue) isSettingValue_Value() {} - -func (*SettingValue_IntValue) isSettingValue_Value() {} - -func (*SettingValue_BytesValue) isSettingValue_Value() {} - -// Effective setting value and the scope it was resolved from. -type EffectiveSetting struct { - state protoimpl.MessageState `protogen:"open.v1"` - Value *SettingValue `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` - Scope SettingScope `protobuf:"varint,2,opt,name=scope,proto3,enum=openshell.sandbox.v1.SettingScope" json:"scope,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *EffectiveSetting) Reset() { - *x = EffectiveSetting{} - mi := &file_sandbox_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *EffectiveSetting) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EffectiveSetting) ProtoMessage() {} - -func (x *EffectiveSetting) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[20] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EffectiveSetting.ProtoReflect.Descriptor instead. -func (*EffectiveSetting) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{20} -} - -func (x *EffectiveSetting) GetValue() *SettingValue { - if x != nil { - return x.Value - } - return nil -} - -func (x *EffectiveSetting) GetScope() SettingScope { - if x != nil { - return x.Scope - } - return SettingScope_SETTING_SCOPE_UNSPECIFIED -} - -// Complete effective sandbox configuration delivered to a supervisor. Fields -// have the same meaning as the identically numbered GetSandboxConfigResponse -// fields, including fail-closed handling of an unknown -// policy_validation_failure_mode and treating absent configuration_admitted as -// not admitted. Move those field comments here when the polling RPC is removed. -type SandboxConfigSnapshot struct { - state protoimpl.MessageState `protogen:"open.v1"` - Policy *SandboxPolicy `protobuf:"bytes,1,opt,name=policy,proto3" json:"policy,omitempty"` - Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` - PolicyHash string `protobuf:"bytes,3,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - Settings map[string]*EffectiveSetting `protobuf:"bytes,4,rep,name=settings,proto3" json:"settings,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - ConfigRevision uint64 `protobuf:"varint,5,opt,name=config_revision,json=configRevision,proto3" json:"config_revision,omitempty"` - PolicySource PolicySource `protobuf:"varint,6,opt,name=policy_source,json=policySource,proto3,enum=openshell.sandbox.v1.PolicySource" json:"policy_source,omitempty"` - GlobalPolicyVersion uint32 `protobuf:"varint,7,opt,name=global_policy_version,json=globalPolicyVersion,proto3" json:"global_policy_version,omitempty"` - ProviderEnvRevision uint64 `protobuf:"varint,8,opt,name=provider_env_revision,json=providerEnvRevision,proto3" json:"provider_env_revision,omitempty"` - SupervisorMiddlewareServices []*SupervisorMiddlewareService `protobuf:"bytes,9,rep,name=supervisor_middleware_services,json=supervisorMiddlewareServices,proto3" json:"supervisor_middleware_services,omitempty"` - Workspace string `protobuf:"bytes,10,opt,name=workspace,proto3" json:"workspace,omitempty"` - PolicyValidationFailureMode string `protobuf:"bytes,11,opt,name=policy_validation_failure_mode,json=policyValidationFailureMode,proto3" json:"policy_validation_failure_mode,omitempty"` - ExtensionAuthenticationEnabled bool `protobuf:"varint,12,opt,name=extension_authentication_enabled,json=extensionAuthenticationEnabled,proto3" json:"extension_authentication_enabled,omitempty"` - ConfigurationAdmitted bool `protobuf:"varint,13,opt,name=configuration_admitted,json=configurationAdmitted,proto3" json:"configuration_admitted,omitempty"` - ProviderAttachmentEpoch string `protobuf:"bytes,14,opt,name=provider_attachment_epoch,json=providerAttachmentEpoch,proto3" json:"provider_attachment_epoch,omitempty"` - ConfigurationInstanceId string `protobuf:"bytes,15,opt,name=configuration_instance_id,json=configurationInstanceId,proto3" json:"configuration_instance_id,omitempty"` - ConfigurationError string `protobuf:"bytes,16,opt,name=configuration_error,json=configurationError,proto3" json:"configuration_error,omitempty"` - SettingsRevision uint64 `protobuf:"varint,17,opt,name=settings_revision,json=settingsRevision,proto3" json:"settings_revision,omitempty"` - // Explicit application ingress allowlist. Absent means no app routes. - ServiceRoutes []*SandboxServiceRoute `protobuf:"bytes,18,rep,name=service_routes,json=serviceRoutes,proto3" json:"service_routes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxConfigSnapshot) Reset() { - *x = SandboxConfigSnapshot{} - mi := &file_sandbox_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxConfigSnapshot) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxConfigSnapshot) ProtoMessage() {} - -func (x *SandboxConfigSnapshot) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[21] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxConfigSnapshot.ProtoReflect.Descriptor instead. -func (*SandboxConfigSnapshot) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{21} -} - -func (x *SandboxConfigSnapshot) GetPolicy() *SandboxPolicy { - if x != nil { - return x.Policy - } - return nil -} - -func (x *SandboxConfigSnapshot) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *SandboxConfigSnapshot) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *SandboxConfigSnapshot) GetSettings() map[string]*EffectiveSetting { - if x != nil { - return x.Settings - } - return nil -} - -func (x *SandboxConfigSnapshot) GetConfigRevision() uint64 { - if x != nil { - return x.ConfigRevision - } - return 0 -} - -func (x *SandboxConfigSnapshot) GetPolicySource() PolicySource { - if x != nil { - return x.PolicySource - } - return PolicySource_POLICY_SOURCE_UNSPECIFIED -} - -func (x *SandboxConfigSnapshot) GetGlobalPolicyVersion() uint32 { - if x != nil { - return x.GlobalPolicyVersion - } - return 0 -} - -func (x *SandboxConfigSnapshot) GetProviderEnvRevision() uint64 { - if x != nil { - return x.ProviderEnvRevision - } - return 0 -} - -func (x *SandboxConfigSnapshot) GetSupervisorMiddlewareServices() []*SupervisorMiddlewareService { - if x != nil { - return x.SupervisorMiddlewareServices - } - return nil -} - -func (x *SandboxConfigSnapshot) GetWorkspace() string { - if x != nil { - return x.Workspace - } - return "" -} - -func (x *SandboxConfigSnapshot) GetPolicyValidationFailureMode() string { - if x != nil { - return x.PolicyValidationFailureMode - } - return "" -} - -func (x *SandboxConfigSnapshot) GetExtensionAuthenticationEnabled() bool { - if x != nil { - return x.ExtensionAuthenticationEnabled - } - return false -} - -func (x *SandboxConfigSnapshot) GetConfigurationAdmitted() bool { - if x != nil { - return x.ConfigurationAdmitted - } - return false -} - -func (x *SandboxConfigSnapshot) GetProviderAttachmentEpoch() string { - if x != nil { - return x.ProviderAttachmentEpoch - } - return "" -} - -func (x *SandboxConfigSnapshot) GetConfigurationInstanceId() string { - if x != nil { - return x.ConfigurationInstanceId - } - return "" -} - -func (x *SandboxConfigSnapshot) GetConfigurationError() string { - if x != nil { - return x.ConfigurationError - } - return "" -} - -func (x *SandboxConfigSnapshot) GetSettingsRevision() uint64 { - if x != nil { - return x.SettingsRevision - } - return 0 -} - -func (x *SandboxConfigSnapshot) GetServiceRoutes() []*SandboxServiceRoute { - if x != nil { - return x.ServiceRoutes - } - return nil -} - -// Response containing effective sandbox settings and policy, projected from -// the SandboxConfigSnapshot that push supervisors receive. -type GetSandboxConfigResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The sandbox policy configuration. - Policy *SandboxPolicy `protobuf:"bytes,1,opt,name=policy,proto3" json:"policy,omitempty"` - // Current policy version (monotonically increasing per sandbox). - Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` - // SHA-256 hash of the serialized policy payload. - PolicyHash string `protobuf:"bytes,3,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - // Effective settings resolved for this sandbox, excluding the reserved policy key. - // Registered keys without a configured value are returned with an empty EffectiveSetting.value. - Settings map[string]*EffectiveSetting `protobuf:"bytes,4,rep,name=settings,proto3" json:"settings,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Fingerprint for effective config (policy + settings). Changes when any effective input changes. - ConfigRevision uint64 `protobuf:"varint,5,opt,name=config_revision,json=configRevision,proto3" json:"config_revision,omitempty"` - // Source of the policy payload for this response. - PolicySource PolicySource `protobuf:"varint,6,opt,name=policy_source,json=policySource,proto3,enum=openshell.sandbox.v1.PolicySource" json:"policy_source,omitempty"` - // When policy_source is GLOBAL, the version of the global policy revision. - // Zero when no global policy is active or when policy_source is SANDBOX. - GlobalPolicyVersion uint32 `protobuf:"varint,7,opt,name=global_policy_version,json=globalPolicyVersion,proto3" json:"global_policy_version,omitempty"` - // Fingerprint for provider credential inputs attached to this sandbox. - // Changes when attached provider names or attached provider records change. - ProviderEnvRevision uint64 `protobuf:"varint,8,opt,name=provider_env_revision,json=providerEnvRevision,proto3" json:"provider_env_revision,omitempty"` - // Operator-registered supervisor middleware services required by the - // effective policy. Built-in middleware is not included. - SupervisorMiddlewareServices []*SupervisorMiddlewareService `protobuf:"bytes,9,rep,name=supervisor_middleware_services,json=supervisorMiddlewareServices,proto3" json:"supervisor_middleware_services,omitempty"` - // Workspace the sandbox belongs to. Allows the supervisor to learn its - // workspace context for subsequent workspace-scoped RPCs. - Workspace string `protobuf:"bytes,10,opt,name=workspace,proto3" json:"workspace,omitempty"` - // Gateway-configured posture for rejected policy generations. Valid values - // are "fail_closed" and "retain_last_valid". Unknown or empty values must - // be treated as fail_closed by the supervisor. - PolicyValidationFailureMode string `protobuf:"bytes,11,opt,name=policy_validation_failure_mode,json=policyValidationFailureMode,proto3" json:"policy_validation_failure_mode,omitempty"` - // Whether this gateway can mint authenticated extension credentials. - // False also covers older gateways that do not advertise this capability; - // supervisors preserve their legacy unauthenticated connection behavior. - ExtensionAuthenticationEnabled bool `protobuf:"varint,12,opt,name=extension_authentication_enabled,json=extensionAuthenticationEnabled,proto3" json:"extension_authentication_enabled,omitempty"` - // Gateway-owned attachment identity captured with this desired configuration. - // Compare for equality; reattachment invalidates previous installation evidence. - ProviderAttachmentEpoch string `protobuf:"bytes,14,opt,name=provider_attachment_epoch,json=providerAttachmentEpoch,proto3" json:"provider_attachment_epoch,omitempty"` - // True only after validating this complete policy/provider composition. - // Missing (older gateway) is deliberately not admission. - ConfigurationAdmitted bool `protobuf:"varint,13,opt,name=configuration_admitted,json=configurationAdmitted,proto3" json:"configuration_admitted,omitempty"` - // Bounded, credential-free admission diagnostic. Empty for admitted policy. - ConfigurationError string `protobuf:"bytes,16,opt,name=configuration_error,json=configurationError,proto3" json:"configuration_error,omitempty"` - // Registration fence for a new supervisor; capture once and retain on retry. - ConfigurationInstanceId string `protobuf:"bytes,15,opt,name=configuration_instance_id,json=configurationInstanceId,proto3" json:"configuration_instance_id,omitempty"` - // Monotonic revision of the sandbox-scoped settings row. - SettingsRevision uint64 `protobuf:"varint,17,opt,name=settings_revision,json=settingsRevision,proto3" json:"settings_revision,omitempty"` - ServiceRoutes []*SandboxServiceRoute `protobuf:"bytes,18,rep,name=service_routes,json=serviceRoutes,proto3" json:"service_routes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxConfigResponse) Reset() { - *x = GetSandboxConfigResponse{} - mi := &file_sandbox_proto_msgTypes[22] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxConfigResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxConfigResponse) ProtoMessage() {} - -func (x *GetSandboxConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[22] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxConfigResponse.ProtoReflect.Descriptor instead. -func (*GetSandboxConfigResponse) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{22} -} - -func (x *GetSandboxConfigResponse) GetPolicy() *SandboxPolicy { - if x != nil { - return x.Policy - } - return nil -} - -func (x *GetSandboxConfigResponse) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *GetSandboxConfigResponse) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *GetSandboxConfigResponse) GetSettings() map[string]*EffectiveSetting { - if x != nil { - return x.Settings - } - return nil -} - -func (x *GetSandboxConfigResponse) GetConfigRevision() uint64 { - if x != nil { - return x.ConfigRevision - } - return 0 -} - -func (x *GetSandboxConfigResponse) GetPolicySource() PolicySource { - if x != nil { - return x.PolicySource - } - return PolicySource_POLICY_SOURCE_UNSPECIFIED -} - -func (x *GetSandboxConfigResponse) GetGlobalPolicyVersion() uint32 { - if x != nil { - return x.GlobalPolicyVersion - } - return 0 -} - -func (x *GetSandboxConfigResponse) GetProviderEnvRevision() uint64 { - if x != nil { - return x.ProviderEnvRevision - } - return 0 -} - -func (x *GetSandboxConfigResponse) GetSupervisorMiddlewareServices() []*SupervisorMiddlewareService { - if x != nil { - return x.SupervisorMiddlewareServices - } - return nil -} - -func (x *GetSandboxConfigResponse) GetWorkspace() string { - if x != nil { - return x.Workspace - } - return "" -} - -func (x *GetSandboxConfigResponse) GetPolicyValidationFailureMode() string { - if x != nil { - return x.PolicyValidationFailureMode - } - return "" -} - -func (x *GetSandboxConfigResponse) GetExtensionAuthenticationEnabled() bool { - if x != nil { - return x.ExtensionAuthenticationEnabled - } - return false -} - -func (x *GetSandboxConfigResponse) GetProviderAttachmentEpoch() string { - if x != nil { - return x.ProviderAttachmentEpoch - } - return "" -} - -func (x *GetSandboxConfigResponse) GetConfigurationAdmitted() bool { - if x != nil { - return x.ConfigurationAdmitted - } - return false -} - -func (x *GetSandboxConfigResponse) GetConfigurationError() string { - if x != nil { - return x.ConfigurationError - } - return "" -} - -func (x *GetSandboxConfigResponse) GetConfigurationInstanceId() string { - if x != nil { - return x.ConfigurationInstanceId - } - return "" -} - -func (x *GetSandboxConfigResponse) GetSettingsRevision() uint64 { - if x != nil { - return x.SettingsRevision - } - return 0 -} - -func (x *GetSandboxConfigResponse) GetServiceRoutes() []*SandboxServiceRoute { - if x != nil { - return x.ServiceRoutes - } - return nil -} - -// Gateway-declared service installed by a supervisor; this does not grant -// access to arbitrary workload ports or assert application health. -type SandboxServiceRoute struct { - state protoimpl.MessageState `protogen:"open.v1"` - EndpointId string `protobuf:"bytes,1,opt,name=endpoint_id,json=endpointId,proto3" json:"endpoint_id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - TargetPort uint32 `protobuf:"varint,3,opt,name=target_port,json=targetPort,proto3" json:"target_port,omitempty"` - AuthorizationMode int32 `protobuf:"varint,4,opt,name=authorization_mode,json=authorizationMode,proto3" json:"authorization_mode,omitempty"` - ResourceVersion uint64 `protobuf:"varint,5,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxServiceRoute) Reset() { - *x = SandboxServiceRoute{} - mi := &file_sandbox_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxServiceRoute) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxServiceRoute) ProtoMessage() {} - -func (x *SandboxServiceRoute) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[23] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxServiceRoute.ProtoReflect.Descriptor instead. -func (*SandboxServiceRoute) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{23} -} - -func (x *SandboxServiceRoute) GetEndpointId() string { - if x != nil { - return x.EndpointId - } - return "" -} - -func (x *SandboxServiceRoute) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *SandboxServiceRoute) GetTargetPort() uint32 { - if x != nil { - return x.TargetPort - } - return 0 -} - -func (x *SandboxServiceRoute) GetAuthorizationMode() int32 { - if x != nil { - return x.AuthorizationMode - } - return 0 -} - -func (x *SandboxServiceRoute) GetResourceVersion() uint64 { - if x != nil { - return x.ResourceVersion - } - return 0 -} - -// Connection details for one operator-registered supervisor middleware service. -// V1 supports plaintext and server-authenticated TLS gRPC. -type SupervisorMiddlewareService struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Operator-owned registration name used by policy attachments and diagnostics. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // gRPC endpoint reachable from the sandbox supervisor. - GrpcEndpoint string `protobuf:"bytes,2,opt,name=grpc_endpoint,json=grpcEndpoint,proto3" json:"grpc_endpoint,omitempty"` - // Operator-owned logical payload limit applied to every binding exposed by - // the service. This caps HTTP bodies and complete WebSocket messages. - MaxPayloadBytes uint64 `protobuf:"varint,3,opt,name=max_payload_bytes,json=maxPayloadBytes,proto3" json:"max_payload_bytes,omitempty"` - // Default RPC timeout for this service. Absence uses the platform default of - // 500ms. Values must be between 10ms and 30s. - RequestTimeout *durationpb.Duration `protobuf:"bytes,104,opt,name=request_timeout,json=requestTimeout,proto3" json:"request_timeout,omitempty"` - // PEM-encoded trust roots loaded by the gateway from the operator-configured - // tls_ca_cert_path. Empty uses the platform trust store. - TlsCaCertPem []byte `protobuf:"bytes,5,opt,name=tls_ca_cert_pem,json=tlsCaCertPem,proto3" json:"tls_ca_cert_pem,omitempty"` - // Exact JWT audience for this service. The gateway resolves an omitted - // operator value to a kind-scoped audience derived from the registration - // name before sending sandbox config. - Audience string `protobuf:"bytes,6,opt,name=audience,proto3" json:"audience,omitempty"` - // Operator opt-out from extension authentication for this registration. - // When true the service may use a plaintext endpoint and OpenShell attaches - // no bearer credential; supervisors must not request one. Intended only for - // trusted-network development deployments. - AllowInsecureTransport bool `protobuf:"varint,7,opt,name=allow_insecure_transport,json=allowInsecureTransport,proto3" json:"allow_insecure_transport,omitempty"` - // How the gateway and supervisor authorize the bindings returned by Describe. - // Absence preserves dynamic discovery. A supervisor predating this field - // cannot enforce a binding policy; upgrade supervisors before requiring one. - BindingPolicy SupervisorMiddlewareBindingPolicy `protobuf:"varint,8,opt,name=binding_policy,json=bindingPolicy,proto3,enum=openshell.sandbox.v1.SupervisorMiddlewareBindingPolicy" json:"binding_policy,omitempty"` - // Operation/phase pairs required by an allowlist or exact policy. Every - // listed binding must be advertised, and only listed bindings may run. - Bindings []SupervisorMiddlewareBinding `protobuf:"varint,9,rep,packed,name=bindings,proto3,enum=openshell.sandbox.v1.SupervisorMiddlewareBinding" json:"bindings,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SupervisorMiddlewareService) Reset() { - *x = SupervisorMiddlewareService{} - mi := &file_sandbox_proto_msgTypes[24] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SupervisorMiddlewareService) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SupervisorMiddlewareService) ProtoMessage() {} - -func (x *SupervisorMiddlewareService) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[24] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SupervisorMiddlewareService.ProtoReflect.Descriptor instead. -func (*SupervisorMiddlewareService) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{24} -} - -func (x *SupervisorMiddlewareService) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *SupervisorMiddlewareService) GetGrpcEndpoint() string { - if x != nil { - return x.GrpcEndpoint - } - return "" -} - -func (x *SupervisorMiddlewareService) GetMaxPayloadBytes() uint64 { - if x != nil { - return x.MaxPayloadBytes - } - return 0 -} - -func (x *SupervisorMiddlewareService) GetRequestTimeout() *durationpb.Duration { - if x != nil { - return x.RequestTimeout - } - return nil -} - -func (x *SupervisorMiddlewareService) GetTlsCaCertPem() []byte { - if x != nil { - return x.TlsCaCertPem - } - return nil -} - -func (x *SupervisorMiddlewareService) GetAudience() string { - if x != nil { - return x.Audience - } - return "" -} - -func (x *SupervisorMiddlewareService) GetAllowInsecureTransport() bool { - if x != nil { - return x.AllowInsecureTransport - } - return false -} - -func (x *SupervisorMiddlewareService) GetBindingPolicy() SupervisorMiddlewareBindingPolicy { - if x != nil { - return x.BindingPolicy - } - return SupervisorMiddlewareBindingPolicy_SUPERVISOR_MIDDLEWARE_BINDING_POLICY_UNSPECIFIED -} - -func (x *SupervisorMiddlewareService) GetBindings() []SupervisorMiddlewareBinding { - if x != nil { - return x.Bindings - } - return nil -} - -var File_sandbox_proto protoreflect.FileDescriptor - -const file_sandbox_proto_rawDesc = "" + - "\n" + - "\rsandbox.proto\x12\x14openshell.sandbox.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x0fdatamodel.proto\"\xa8\x05\n" + - "\rSandboxPolicy\x12\x18\n" + - "\aversion\x18\x01 \x01(\rR\aversion\x12F\n" + - "\n" + - "filesystem\x18\x02 \x01(\v2&.openshell.sandbox.v1.FilesystemPolicyR\n" + - "filesystem\x12@\n" + - "\blandlock\x18\x03 \x01(\v2$.openshell.sandbox.v1.LandlockPolicyR\blandlock\x12=\n" + - "\aprocess\x18\x04 \x01(\v2#.openshell.sandbox.v1.ProcessPolicyR\aprocess\x12c\n" + - "\x10network_policies\x18\x05 \x03(\v28.openshell.sandbox.v1.SandboxPolicy.NetworkPoliciesEntryR\x0fnetworkPolicies\x12l\n" + - "\x13network_middlewares\x18\x06 \x03(\v2;.openshell.sandbox.v1.SandboxPolicy.NetworkMiddlewaresEntryR\x12networkMiddlewares\x1ak\n" + - "\x14NetworkPoliciesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12=\n" + - "\x05value\x18\x02 \x01(\v2'.openshell.sandbox.v1.NetworkPolicyRuleR\x05value:\x028\x01\x1at\n" + - "\x17NetworkMiddlewaresEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12C\n" + - "\x05value\x18\x02 \x01(\v2-.openshell.sandbox.v1.NetworkMiddlewareConfigR\x05value:\x028\x01\"w\n" + - "\x10FilesystemPolicy\x12'\n" + - "\x0finclude_workdir\x18\x01 \x01(\bR\x0eincludeWorkdir\x12\x1b\n" + - "\tread_only\x18\x02 \x03(\tR\breadOnly\x12\x1d\n" + - "\n" + - "read_write\x18\x03 \x03(\tR\treadWrite\"6\n" + - "\x0eLandlockPolicy\x12$\n" + - "\rcompatibility\x18\x01 \x01(\tR\rcompatibility\"Q\n" + - "\rProcessPolicy\x12\x1e\n" + - "\vrun_as_user\x18\x01 \x01(\tR\trunAsUser\x12 \n" + - "\frun_as_group\x18\x02 \x01(\tR\n" + - "runAsGroup\"\xad\x01\n" + - "\x11NetworkPolicyRule\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12C\n" + - "\tendpoints\x18\x02 \x03(\v2%.openshell.sandbox.v1.NetworkEndpointR\tendpoints\x12?\n" + - "\bbinaries\x18\x03 \x03(\v2#.openshell.sandbox.v1.NetworkBinaryR\bbinaries\"\xff\x01\n" + - "\x17NetworkMiddlewareConfig\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12\x1e\n" + - "\n" + - "middleware\x18\x02 \x01(\tR\n" + - "middleware\x12/\n" + - "\x06config\x18\x03 \x01(\v2\x17.google.protobuf.StructR\x06config\x12\x19\n" + - "\bon_error\x18\x04 \x01(\tR\aonError\x12N\n" + - "\tendpoints\x18\x05 \x01(\v20.openshell.sandbox.v1.MiddlewareEndpointSelectorR\tendpoints\x12\x14\n" + - "\x05order\x18\x06 \x01(\x05R\x05order\"P\n" + - "\x1aMiddlewareEndpointSelector\x12\x18\n" + - "\ainclude\x18\x01 \x03(\tR\ainclude\x12\x18\n" + - "\aexclude\x18\x02 \x03(\tR\aexclude\"6\n" + - "\x18NetworkCredentialBinding\x12\x1a\n" + - "\bprovider\x18\x01 \x01(\tR\bprovider\"\x87\f\n" + - "\x0fNetworkEndpoint\x12\x12\n" + - "\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\x02 \x01(\rR\x04port\x12\x1a\n" + - "\bprotocol\x18\x03 \x01(\tR\bprotocol\x126\n" + - "\x03tls\x18\x04 \x01(\x0e2$.openshell.sandbox.v1.NetworkTlsModeR\x03tls\x12N\n" + - "\venforcement\x18\x05 \x01(\x0e2,.openshell.sandbox.v1.NetworkEnforcementModeR\venforcement\x12A\n" + - "\x06access\x18\x06 \x01(\x0e2).openshell.sandbox.v1.NetworkAccessPresetR\x06access\x122\n" + - "\x05rules\x18\a \x03(\v2\x1c.openshell.sandbox.v1.L7RuleR\x05rules\x12\x1f\n" + - "\vallowed_ips\x18\b \x03(\tR\n" + - "allowedIps\x12\x14\n" + - "\x05ports\x18\t \x03(\rR\x05ports\x12?\n" + - "\n" + - "deny_rules\x18\n" + - " \x03(\v2 .openshell.sandbox.v1.L7DenyRuleR\tdenyRules\x12.\n" + - "\x13allow_encoded_slash\x18\v \x01(\bR\x11allowEncodedSlash\x12+\n" + - "\x11persisted_queries\x18\f \x01(\tR\x10persistedQueries\x12~\n" + - "\x19graphql_persisted_queries\x18\r \x03(\v2B.openshell.sandbox.v1.NetworkEndpoint.GraphqlPersistedQueriesEntryR\x17graphqlPersistedQueries\x123\n" + - "\x16graphql_max_body_bytes\x18\x0e \x01(\rR\x13graphqlMaxBodyBytes\x12\x12\n" + - "\x04path\x18\x0f \x01(\tR\x04path\x12@\n" + - "\x1cwebsocket_credential_rewrite\x18\x10 \x01(\bR\x1awebsocketCredentialRewrite\x12E\n" + - "\x1frequest_body_credential_rewrite\x18\x11 \x01(\bR\x1crequestBodyCredentialRewrite\x12)\n" + - "\x10advisor_proposed\x18\x12 \x01(\bR\x0fadvisorProposed\x12-\n" + - "\x12credential_signing\x18\x13 \x01(\tR\x11credentialSigning\x12'\n" + - "\x0fsigning_service\x18\x14 \x01(\tR\x0esigningService\x12%\n" + - "\x0esigning_region\x18\x15 \x01(\tR\rsigningRegion\x124\n" + - "\x17json_rpc_max_body_bytes\x18\x16 \x01(\rR\x13jsonRpcMaxBodyBytes\x122\n" + - "\x03mcp\x18\x17 \x01(\v2 .openshell.sandbox.v1.McpOptionsR\x03mcp\x12]\n" + - "\x12credential_binding\x18\x18 \x01(\v2..openshell.sandbox.v1.NetworkCredentialBindingR\x11credentialBinding\x12B\n" + - "\x1dallow_uninspected_credentials\x18\x19 \x01(\bR\x1ballowUninspectedCredentials\x123\n" + - "\x15provider_credentialed\x18\x1a \x01(\bR\x14providerCredentialed\x12*\n" + - "\x11token_grant_owner\x18\x1b \x01(\tR\x0ftokenGrantOwner\x1ar\n" + - "\x1cGraphqlPersistedQueriesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12<\n" + - "\x05value\x18\x02 \x01(\v2&.openshell.sandbox.v1.GraphqlOperationR\x05value:\x028\x01\"\xd2\x01\n" + - "\n" + - "McpOptions\x12/\n" + - "\x11strict_tool_names\x18\x01 \x01(\bH\x00R\x0fstrictToolNames\x88\x01\x01\x12A\n" + - "\x1ballow_all_known_mcp_methods\x18\x02 \x01(\bH\x01R\x17allowAllKnownMcpMethods\x88\x01\x01\x12\x1a\n" + - "\bversions\x18\x03 \x03(\tR\bversionsB\x14\n" + - "\x12_strict_tool_namesB\x1e\n" + - "\x1c_allow_all_known_mcp_methods\"x\n" + - "\x10GraphqlOperation\x12%\n" + - "\x0eoperation_type\x18\x01 \x01(\tR\roperationType\x12%\n" + - "\x0eoperation_name\x18\x02 \x01(\tR\roperationName\x12\x16\n" + - "\x06fields\x18\x03 \x03(\tR\x06fields\"\x88\x04\n" + - "\n" + - "L7DenyRule\x12\x16\n" + - "\x06method\x18\x01 \x01(\tR\x06method\x12\x12\n" + - "\x04path\x18\x02 \x01(\tR\x04path\x12\x18\n" + - "\acommand\x18\x03 \x01(\tR\acommand\x12A\n" + - "\x05query\x18\x04 \x03(\v2+.openshell.sandbox.v1.L7DenyRule.QueryEntryR\x05query\x12%\n" + - "\x0eoperation_type\x18\x05 \x01(\tR\roperationType\x12%\n" + - "\x0eoperation_name\x18\x06 \x01(\tR\roperationName\x12\x16\n" + - "\x06fields\x18\a \x03(\tR\x06fields\x12D\n" + - "\x06params\x18\t \x03(\v2,.openshell.sandbox.v1.L7DenyRule.ParamsEntryR\x06params\x1a^\n" + - "\n" + - "QueryEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12:\n" + - "\x05value\x18\x02 \x01(\v2$.openshell.sandbox.v1.L7QueryMatcherR\x05value:\x028\x01\x1a_\n" + - "\vParamsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12:\n" + - "\x05value\x18\x02 \x01(\v2$.openshell.sandbox.v1.L7QueryMatcherR\x05value:\x028\x01J\x04\b\b\x10\t\"=\n" + - "\x06L7Rule\x123\n" + - "\x05allow\x18\x01 \x01(\v2\x1d.openshell.sandbox.v1.L7AllowR\x05allow\"\xff\x03\n" + - "\aL7Allow\x12\x16\n" + - "\x06method\x18\x01 \x01(\tR\x06method\x12\x12\n" + - "\x04path\x18\x02 \x01(\tR\x04path\x12\x18\n" + - "\acommand\x18\x03 \x01(\tR\acommand\x12>\n" + - "\x05query\x18\x04 \x03(\v2(.openshell.sandbox.v1.L7Allow.QueryEntryR\x05query\x12%\n" + - "\x0eoperation_type\x18\x05 \x01(\tR\roperationType\x12%\n" + - "\x0eoperation_name\x18\x06 \x01(\tR\roperationName\x12\x16\n" + - "\x06fields\x18\a \x03(\tR\x06fields\x12A\n" + - "\x06params\x18\t \x03(\v2).openshell.sandbox.v1.L7Allow.ParamsEntryR\x06params\x1a^\n" + - "\n" + - "QueryEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12:\n" + - "\x05value\x18\x02 \x01(\v2$.openshell.sandbox.v1.L7QueryMatcherR\x05value:\x028\x01\x1a_\n" + - "\vParamsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12:\n" + - "\x05value\x18\x02 \x01(\v2$.openshell.sandbox.v1.L7QueryMatcherR\x05value:\x028\x01J\x04\b\b\x10\t\"6\n" + - "\x0eL7QueryMatcher\x12\x12\n" + - "\x04glob\x18\x01 \x01(\tR\x04glob\x12\x10\n" + - "\x03any\x18\x02 \x03(\tR\x03any\"2\n" + - "\rNetworkBinary\x12\x12\n" + - "\x04path\x18\x01 \x01(\tR\x04pathJ\x04\b\x02\x10\x03R\aharness\"\x81\x01\n" + - "\x17GetSandboxConfigRequest\x12R\n" + - "\x0fworkspace_scope\x18\x03 \x01(\v2).openshell.datamodel.v1.WorkspaceSelectorR\x0eworkspaceScope\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"\x19\n" + - "\x17GetGatewayConfigRequest\"\x82\x02\n" + - "\x18GetGatewayConfigResponse\x12X\n" + - "\bsettings\x18\x01 \x03(\v2<.openshell.sandbox.v1.GetGatewayConfigResponse.SettingsEntryR\bsettings\x12+\n" + - "\x11settings_revision\x18\x02 \x01(\x04R\x10settingsRevision\x1a_\n" + - "\rSettingsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x128\n" + - "\x05value\x18\x02 \x01(\v2\".openshell.sandbox.v1.SettingValueR\x05value:\x028\x01\"\x9f\x01\n" + - "\fSettingValue\x12#\n" + - "\fstring_value\x18\x01 \x01(\tH\x00R\vstringValue\x12\x1f\n" + - "\n" + - "bool_value\x18\x02 \x01(\bH\x00R\tboolValue\x12\x1d\n" + - "\tint_value\x18\x03 \x01(\x03H\x00R\bintValue\x12!\n" + - "\vbytes_value\x18\x04 \x01(\fH\x00R\n" + - "bytesValueB\a\n" + - "\x05value\"\x86\x01\n" + - "\x10EffectiveSetting\x128\n" + - "\x05value\x18\x01 \x01(\v2\".openshell.sandbox.v1.SettingValueR\x05value\x128\n" + - "\x05scope\x18\x02 \x01(\x0e2\".openshell.sandbox.v1.SettingScopeR\x05scope\"\xaa\t\n" + - "\x15SandboxConfigSnapshot\x12;\n" + - "\x06policy\x18\x01 \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x06policy\x12\x18\n" + - "\aversion\x18\x02 \x01(\rR\aversion\x12\x1f\n" + - "\vpolicy_hash\x18\x03 \x01(\tR\n" + - "policyHash\x12U\n" + - "\bsettings\x18\x04 \x03(\v29.openshell.sandbox.v1.SandboxConfigSnapshot.SettingsEntryR\bsettings\x12'\n" + - "\x0fconfig_revision\x18\x05 \x01(\x04R\x0econfigRevision\x12G\n" + - "\rpolicy_source\x18\x06 \x01(\x0e2\".openshell.sandbox.v1.PolicySourceR\fpolicySource\x122\n" + - "\x15global_policy_version\x18\a \x01(\rR\x13globalPolicyVersion\x122\n" + - "\x15provider_env_revision\x18\b \x01(\x04R\x13providerEnvRevision\x12w\n" + - "\x1esupervisor_middleware_services\x18\t \x03(\v21.openshell.sandbox.v1.SupervisorMiddlewareServiceR\x1csupervisorMiddlewareServices\x12\x1c\n" + - "\tworkspace\x18\n" + - " \x01(\tR\tworkspace\x12C\n" + - "\x1epolicy_validation_failure_mode\x18\v \x01(\tR\x1bpolicyValidationFailureMode\x12H\n" + - " extension_authentication_enabled\x18\f \x01(\bR\x1eextensionAuthenticationEnabled\x125\n" + - "\x16configuration_admitted\x18\r \x01(\bR\x15configurationAdmitted\x12:\n" + - "\x19provider_attachment_epoch\x18\x0e \x01(\tR\x17providerAttachmentEpoch\x12:\n" + - "\x19configuration_instance_id\x18\x0f \x01(\tR\x17configurationInstanceId\x12/\n" + - "\x13configuration_error\x18\x10 \x01(\tR\x12configurationError\x12+\n" + - "\x11settings_revision\x18\x11 \x01(\x04R\x10settingsRevision\x12P\n" + - "\x0eservice_routes\x18\x12 \x03(\v2).openshell.sandbox.v1.SandboxServiceRouteR\rserviceRoutes\x1ac\n" + - "\rSettingsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12<\n" + - "\x05value\x18\x02 \x01(\v2&.openshell.sandbox.v1.EffectiveSettingR\x05value:\x028\x01\"\xb0\t\n" + - "\x18GetSandboxConfigResponse\x12;\n" + - "\x06policy\x18\x01 \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x06policy\x12\x18\n" + - "\aversion\x18\x02 \x01(\rR\aversion\x12\x1f\n" + - "\vpolicy_hash\x18\x03 \x01(\tR\n" + - "policyHash\x12X\n" + - "\bsettings\x18\x04 \x03(\v2<.openshell.sandbox.v1.GetSandboxConfigResponse.SettingsEntryR\bsettings\x12'\n" + - "\x0fconfig_revision\x18\x05 \x01(\x04R\x0econfigRevision\x12G\n" + - "\rpolicy_source\x18\x06 \x01(\x0e2\".openshell.sandbox.v1.PolicySourceR\fpolicySource\x122\n" + - "\x15global_policy_version\x18\a \x01(\rR\x13globalPolicyVersion\x122\n" + - "\x15provider_env_revision\x18\b \x01(\x04R\x13providerEnvRevision\x12w\n" + - "\x1esupervisor_middleware_services\x18\t \x03(\v21.openshell.sandbox.v1.SupervisorMiddlewareServiceR\x1csupervisorMiddlewareServices\x12\x1c\n" + - "\tworkspace\x18\n" + - " \x01(\tR\tworkspace\x12C\n" + - "\x1epolicy_validation_failure_mode\x18\v \x01(\tR\x1bpolicyValidationFailureMode\x12H\n" + - " extension_authentication_enabled\x18\f \x01(\bR\x1eextensionAuthenticationEnabled\x12:\n" + - "\x19provider_attachment_epoch\x18\x0e \x01(\tR\x17providerAttachmentEpoch\x125\n" + - "\x16configuration_admitted\x18\r \x01(\bR\x15configurationAdmitted\x12/\n" + - "\x13configuration_error\x18\x10 \x01(\tR\x12configurationError\x12:\n" + - "\x19configuration_instance_id\x18\x0f \x01(\tR\x17configurationInstanceId\x12+\n" + - "\x11settings_revision\x18\x11 \x01(\x04R\x10settingsRevision\x12P\n" + - "\x0eservice_routes\x18\x12 \x03(\v2).openshell.sandbox.v1.SandboxServiceRouteR\rserviceRoutes\x1ac\n" + - "\rSettingsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12<\n" + - "\x05value\x18\x02 \x01(\v2&.openshell.sandbox.v1.EffectiveSettingR\x05value:\x028\x01\"\xc5\x01\n" + - "\x13SandboxServiceRoute\x12\x1f\n" + - "\vendpoint_id\x18\x01 \x01(\tR\n" + - "endpointId\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\x12\x1f\n" + - "\vtarget_port\x18\x03 \x01(\rR\n" + - "targetPort\x12-\n" + - "\x12authorization_mode\x18\x04 \x01(\x05R\x11authorizationMode\x12)\n" + - "\x10resource_version\x18\x05 \x01(\x04R\x0fresourceVersion\"\x81\x04\n" + - "\x1bSupervisorMiddlewareService\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12#\n" + - "\rgrpc_endpoint\x18\x02 \x01(\tR\fgrpcEndpoint\x12*\n" + - "\x11max_payload_bytes\x18\x03 \x01(\x04R\x0fmaxPayloadBytes\x12B\n" + - "\x0frequest_timeout\x18h \x01(\v2\x19.google.protobuf.DurationR\x0erequestTimeout\x12%\n" + - "\x0ftls_ca_cert_pem\x18\x05 \x01(\fR\ftlsCaCertPem\x12\x1a\n" + - "\baudience\x18\x06 \x01(\tR\baudience\x128\n" + - "\x18allow_insecure_transport\x18\a \x01(\bR\x16allowInsecureTransport\x12^\n" + - "\x0ebinding_policy\x18\b \x01(\x0e27.openshell.sandbox.v1.SupervisorMiddlewareBindingPolicyR\rbindingPolicy\x12M\n" + - "\bbindings\x18\t \x03(\x0e21.openshell.sandbox.v1.SupervisorMiddlewareBindingR\bbindingsJ\x04\b\x04\x10\x05R\atimeout*\x97\x01\n" + - "\x0eNetworkTlsMode\x12 \n" + - "\x1cNETWORK_TLS_MODE_UNSPECIFIED\x10\x00\x12\x19\n" + - "\x15NETWORK_TLS_MODE_SKIP\x10\x01\x12\"\n" + - "\x1aNETWORK_TLS_MODE_TERMINATE\x10\x02\x1a\x02\b\x01\x12$\n" + - "\x1cNETWORK_TLS_MODE_PASSTHROUGH\x10\x03\x1a\x02\b\x01*\x8c\x01\n" + - "\x16NetworkEnforcementMode\x12(\n" + - "$NETWORK_ENFORCEMENT_MODE_UNSPECIFIED\x10\x00\x12$\n" + - " NETWORK_ENFORCEMENT_MODE_ENFORCE\x10\x01\x12\"\n" + - "\x1eNETWORK_ENFORCEMENT_MODE_AUDIT\x10\x02*\xa7\x01\n" + - "\x13NetworkAccessPreset\x12%\n" + - "!NETWORK_ACCESS_PRESET_UNSPECIFIED\x10\x00\x12#\n" + - "\x1fNETWORK_ACCESS_PRESET_READ_ONLY\x10\x01\x12$\n" + - " NETWORK_ACCESS_PRESET_READ_WRITE\x10\x02\x12\x1e\n" + - "\x1aNETWORK_ACCESS_PRESET_FULL\x10\x03*b\n" + - "\fSettingScope\x12\x1d\n" + - "\x19SETTING_SCOPE_UNSPECIFIED\x10\x00\x12\x19\n" + - "\x15SETTING_SCOPE_SANDBOX\x10\x01\x12\x18\n" + - "\x14SETTING_SCOPE_GLOBAL\x10\x02*b\n" + - "\fPolicySource\x12\x1d\n" + - "\x19POLICY_SOURCE_UNSPECIFIED\x10\x00\x12\x19\n" + - "\x15POLICY_SOURCE_SANDBOX\x10\x01\x12\x18\n" + - "\x14POLICY_SOURCE_GLOBAL\x10\x02*\xef\x01\n" + - "!SupervisorMiddlewareBindingPolicy\x124\n" + - "0SUPERVISOR_MIDDLEWARE_BINDING_POLICY_UNSPECIFIED\x10\x00\x120\n" + - ",SUPERVISOR_MIDDLEWARE_BINDING_POLICY_DYNAMIC\x10\x01\x122\n" + - ".SUPERVISOR_MIDDLEWARE_BINDING_POLICY_ALLOWLIST\x10\x02\x12.\n" + - "*SUPERVISOR_MIDDLEWARE_BINDING_POLICY_EXACT\x10\x03*\x8d\x02\n" + - "\x1bSupervisorMiddlewareBinding\x12-\n" + - ")SUPERVISOR_MIDDLEWARE_BINDING_UNSPECIFIED\x10\x00\x12>\n" + - ":SUPERVISOR_MIDDLEWARE_BINDING_HTTP_REQUEST_PRE_CREDENTIALS\x10\x01\x12:\n" + - "6SUPERVISOR_MIDDLEWARE_BINDING_HTTP_RESPONSE_PRE_RETURN\x10\x02\x12C\n" + - "?SUPERVISOR_MIDDLEWARE_BINDING_WEBSOCKET_MESSAGE_PRE_CREDENTIALS\x10\x03b\x06proto3" - -var ( - file_sandbox_proto_rawDescOnce sync.Once - file_sandbox_proto_rawDescData []byte -) - -func file_sandbox_proto_rawDescGZIP() []byte { - file_sandbox_proto_rawDescOnce.Do(func() { - file_sandbox_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_sandbox_proto_rawDesc), len(file_sandbox_proto_rawDesc))) - }) - return file_sandbox_proto_rawDescData -} - -var file_sandbox_proto_enumTypes = make([]protoimpl.EnumInfo, 7) -var file_sandbox_proto_msgTypes = make([]protoimpl.MessageInfo, 35) -var file_sandbox_proto_goTypes = []any{ - (NetworkTlsMode)(0), // 0: openshell.sandbox.v1.NetworkTlsMode - (NetworkEnforcementMode)(0), // 1: openshell.sandbox.v1.NetworkEnforcementMode - (NetworkAccessPreset)(0), // 2: openshell.sandbox.v1.NetworkAccessPreset - (SettingScope)(0), // 3: openshell.sandbox.v1.SettingScope - (PolicySource)(0), // 4: openshell.sandbox.v1.PolicySource - (SupervisorMiddlewareBindingPolicy)(0), // 5: openshell.sandbox.v1.SupervisorMiddlewareBindingPolicy - (SupervisorMiddlewareBinding)(0), // 6: openshell.sandbox.v1.SupervisorMiddlewareBinding - (*SandboxPolicy)(nil), // 7: openshell.sandbox.v1.SandboxPolicy - (*FilesystemPolicy)(nil), // 8: openshell.sandbox.v1.FilesystemPolicy - (*LandlockPolicy)(nil), // 9: openshell.sandbox.v1.LandlockPolicy - (*ProcessPolicy)(nil), // 10: openshell.sandbox.v1.ProcessPolicy - (*NetworkPolicyRule)(nil), // 11: openshell.sandbox.v1.NetworkPolicyRule - (*NetworkMiddlewareConfig)(nil), // 12: openshell.sandbox.v1.NetworkMiddlewareConfig - (*MiddlewareEndpointSelector)(nil), // 13: openshell.sandbox.v1.MiddlewareEndpointSelector - (*NetworkCredentialBinding)(nil), // 14: openshell.sandbox.v1.NetworkCredentialBinding - (*NetworkEndpoint)(nil), // 15: openshell.sandbox.v1.NetworkEndpoint - (*McpOptions)(nil), // 16: openshell.sandbox.v1.McpOptions - (*GraphqlOperation)(nil), // 17: openshell.sandbox.v1.GraphqlOperation - (*L7DenyRule)(nil), // 18: openshell.sandbox.v1.L7DenyRule - (*L7Rule)(nil), // 19: openshell.sandbox.v1.L7Rule - (*L7Allow)(nil), // 20: openshell.sandbox.v1.L7Allow - (*L7QueryMatcher)(nil), // 21: openshell.sandbox.v1.L7QueryMatcher - (*NetworkBinary)(nil), // 22: openshell.sandbox.v1.NetworkBinary - (*GetSandboxConfigRequest)(nil), // 23: openshell.sandbox.v1.GetSandboxConfigRequest - (*GetGatewayConfigRequest)(nil), // 24: openshell.sandbox.v1.GetGatewayConfigRequest - (*GetGatewayConfigResponse)(nil), // 25: openshell.sandbox.v1.GetGatewayConfigResponse - (*SettingValue)(nil), // 26: openshell.sandbox.v1.SettingValue - (*EffectiveSetting)(nil), // 27: openshell.sandbox.v1.EffectiveSetting - (*SandboxConfigSnapshot)(nil), // 28: openshell.sandbox.v1.SandboxConfigSnapshot - (*GetSandboxConfigResponse)(nil), // 29: openshell.sandbox.v1.GetSandboxConfigResponse - (*SandboxServiceRoute)(nil), // 30: openshell.sandbox.v1.SandboxServiceRoute - (*SupervisorMiddlewareService)(nil), // 31: openshell.sandbox.v1.SupervisorMiddlewareService - nil, // 32: openshell.sandbox.v1.SandboxPolicy.NetworkPoliciesEntry - nil, // 33: openshell.sandbox.v1.SandboxPolicy.NetworkMiddlewaresEntry - nil, // 34: openshell.sandbox.v1.NetworkEndpoint.GraphqlPersistedQueriesEntry - nil, // 35: openshell.sandbox.v1.L7DenyRule.QueryEntry - nil, // 36: openshell.sandbox.v1.L7DenyRule.ParamsEntry - nil, // 37: openshell.sandbox.v1.L7Allow.QueryEntry - nil, // 38: openshell.sandbox.v1.L7Allow.ParamsEntry - nil, // 39: openshell.sandbox.v1.GetGatewayConfigResponse.SettingsEntry - nil, // 40: openshell.sandbox.v1.SandboxConfigSnapshot.SettingsEntry - nil, // 41: openshell.sandbox.v1.GetSandboxConfigResponse.SettingsEntry - (*structpb.Struct)(nil), // 42: google.protobuf.Struct - (*datamodelv1.WorkspaceSelector)(nil), // 43: openshell.datamodel.v1.WorkspaceSelector - (*durationpb.Duration)(nil), // 44: google.protobuf.Duration -} -var file_sandbox_proto_depIdxs = []int32{ - 8, // 0: openshell.sandbox.v1.SandboxPolicy.filesystem:type_name -> openshell.sandbox.v1.FilesystemPolicy - 9, // 1: openshell.sandbox.v1.SandboxPolicy.landlock:type_name -> openshell.sandbox.v1.LandlockPolicy - 10, // 2: openshell.sandbox.v1.SandboxPolicy.process:type_name -> openshell.sandbox.v1.ProcessPolicy - 32, // 3: openshell.sandbox.v1.SandboxPolicy.network_policies:type_name -> openshell.sandbox.v1.SandboxPolicy.NetworkPoliciesEntry - 33, // 4: openshell.sandbox.v1.SandboxPolicy.network_middlewares:type_name -> openshell.sandbox.v1.SandboxPolicy.NetworkMiddlewaresEntry - 15, // 5: openshell.sandbox.v1.NetworkPolicyRule.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint - 22, // 6: openshell.sandbox.v1.NetworkPolicyRule.binaries:type_name -> openshell.sandbox.v1.NetworkBinary - 42, // 7: openshell.sandbox.v1.NetworkMiddlewareConfig.config:type_name -> google.protobuf.Struct - 13, // 8: openshell.sandbox.v1.NetworkMiddlewareConfig.endpoints:type_name -> openshell.sandbox.v1.MiddlewareEndpointSelector - 0, // 9: openshell.sandbox.v1.NetworkEndpoint.tls:type_name -> openshell.sandbox.v1.NetworkTlsMode - 1, // 10: openshell.sandbox.v1.NetworkEndpoint.enforcement:type_name -> openshell.sandbox.v1.NetworkEnforcementMode - 2, // 11: openshell.sandbox.v1.NetworkEndpoint.access:type_name -> openshell.sandbox.v1.NetworkAccessPreset - 19, // 12: openshell.sandbox.v1.NetworkEndpoint.rules:type_name -> openshell.sandbox.v1.L7Rule - 18, // 13: openshell.sandbox.v1.NetworkEndpoint.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule - 34, // 14: openshell.sandbox.v1.NetworkEndpoint.graphql_persisted_queries:type_name -> openshell.sandbox.v1.NetworkEndpoint.GraphqlPersistedQueriesEntry - 16, // 15: openshell.sandbox.v1.NetworkEndpoint.mcp:type_name -> openshell.sandbox.v1.McpOptions - 14, // 16: openshell.sandbox.v1.NetworkEndpoint.credential_binding:type_name -> openshell.sandbox.v1.NetworkCredentialBinding - 35, // 17: openshell.sandbox.v1.L7DenyRule.query:type_name -> openshell.sandbox.v1.L7DenyRule.QueryEntry - 36, // 18: openshell.sandbox.v1.L7DenyRule.params:type_name -> openshell.sandbox.v1.L7DenyRule.ParamsEntry - 20, // 19: openshell.sandbox.v1.L7Rule.allow:type_name -> openshell.sandbox.v1.L7Allow - 37, // 20: openshell.sandbox.v1.L7Allow.query:type_name -> openshell.sandbox.v1.L7Allow.QueryEntry - 38, // 21: openshell.sandbox.v1.L7Allow.params:type_name -> openshell.sandbox.v1.L7Allow.ParamsEntry - 43, // 22: openshell.sandbox.v1.GetSandboxConfigRequest.workspace_scope:type_name -> openshell.datamodel.v1.WorkspaceSelector - 39, // 23: openshell.sandbox.v1.GetGatewayConfigResponse.settings:type_name -> openshell.sandbox.v1.GetGatewayConfigResponse.SettingsEntry - 26, // 24: openshell.sandbox.v1.EffectiveSetting.value:type_name -> openshell.sandbox.v1.SettingValue - 3, // 25: openshell.sandbox.v1.EffectiveSetting.scope:type_name -> openshell.sandbox.v1.SettingScope - 7, // 26: openshell.sandbox.v1.SandboxConfigSnapshot.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 40, // 27: openshell.sandbox.v1.SandboxConfigSnapshot.settings:type_name -> openshell.sandbox.v1.SandboxConfigSnapshot.SettingsEntry - 4, // 28: openshell.sandbox.v1.SandboxConfigSnapshot.policy_source:type_name -> openshell.sandbox.v1.PolicySource - 31, // 29: openshell.sandbox.v1.SandboxConfigSnapshot.supervisor_middleware_services:type_name -> openshell.sandbox.v1.SupervisorMiddlewareService - 30, // 30: openshell.sandbox.v1.SandboxConfigSnapshot.service_routes:type_name -> openshell.sandbox.v1.SandboxServiceRoute - 7, // 31: openshell.sandbox.v1.GetSandboxConfigResponse.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 41, // 32: openshell.sandbox.v1.GetSandboxConfigResponse.settings:type_name -> openshell.sandbox.v1.GetSandboxConfigResponse.SettingsEntry - 4, // 33: openshell.sandbox.v1.GetSandboxConfigResponse.policy_source:type_name -> openshell.sandbox.v1.PolicySource - 31, // 34: openshell.sandbox.v1.GetSandboxConfigResponse.supervisor_middleware_services:type_name -> openshell.sandbox.v1.SupervisorMiddlewareService - 30, // 35: openshell.sandbox.v1.GetSandboxConfigResponse.service_routes:type_name -> openshell.sandbox.v1.SandboxServiceRoute - 44, // 36: openshell.sandbox.v1.SupervisorMiddlewareService.request_timeout:type_name -> google.protobuf.Duration - 5, // 37: openshell.sandbox.v1.SupervisorMiddlewareService.binding_policy:type_name -> openshell.sandbox.v1.SupervisorMiddlewareBindingPolicy - 6, // 38: openshell.sandbox.v1.SupervisorMiddlewareService.bindings:type_name -> openshell.sandbox.v1.SupervisorMiddlewareBinding - 11, // 39: openshell.sandbox.v1.SandboxPolicy.NetworkPoliciesEntry.value:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 12, // 40: openshell.sandbox.v1.SandboxPolicy.NetworkMiddlewaresEntry.value:type_name -> openshell.sandbox.v1.NetworkMiddlewareConfig - 17, // 41: openshell.sandbox.v1.NetworkEndpoint.GraphqlPersistedQueriesEntry.value:type_name -> openshell.sandbox.v1.GraphqlOperation - 21, // 42: openshell.sandbox.v1.L7DenyRule.QueryEntry.value:type_name -> openshell.sandbox.v1.L7QueryMatcher - 21, // 43: openshell.sandbox.v1.L7DenyRule.ParamsEntry.value:type_name -> openshell.sandbox.v1.L7QueryMatcher - 21, // 44: openshell.sandbox.v1.L7Allow.QueryEntry.value:type_name -> openshell.sandbox.v1.L7QueryMatcher - 21, // 45: openshell.sandbox.v1.L7Allow.ParamsEntry.value:type_name -> openshell.sandbox.v1.L7QueryMatcher - 26, // 46: openshell.sandbox.v1.GetGatewayConfigResponse.SettingsEntry.value:type_name -> openshell.sandbox.v1.SettingValue - 27, // 47: openshell.sandbox.v1.SandboxConfigSnapshot.SettingsEntry.value:type_name -> openshell.sandbox.v1.EffectiveSetting - 27, // 48: openshell.sandbox.v1.GetSandboxConfigResponse.SettingsEntry.value:type_name -> openshell.sandbox.v1.EffectiveSetting - 49, // [49:49] is the sub-list for method output_type - 49, // [49:49] is the sub-list for method input_type - 49, // [49:49] is the sub-list for extension type_name - 49, // [49:49] is the sub-list for extension extendee - 0, // [0:49] is the sub-list for field type_name -} - -func init() { file_sandbox_proto_init() } -func file_sandbox_proto_init() { - if File_sandbox_proto != nil { - return - } - file_sandbox_proto_msgTypes[9].OneofWrappers = []any{} - file_sandbox_proto_msgTypes[19].OneofWrappers = []any{ - (*SettingValue_StringValue)(nil), - (*SettingValue_BoolValue)(nil), - (*SettingValue_IntValue)(nil), - (*SettingValue_BytesValue)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_sandbox_proto_rawDesc), len(file_sandbox_proto_rawDesc)), - NumEnums: 7, - NumMessages: 35, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_sandbox_proto_goTypes, - DependencyIndexes: file_sandbox_proto_depIdxs, - EnumInfos: file_sandbox_proto_enumTypes, - MessageInfos: file_sandbox_proto_msgTypes, - }.Build() - File_sandbox_proto = out.File - file_sandbox_proto_goTypes = nil - file_sandbox_proto_depIdxs = nil -} diff --git a/sdk/go/tests/compatibility/sandbox_updates_test.go b/sdk/go/tests/compatibility/sandbox_updates_test.go deleted file mode 100644 index 44bc7b023f..0000000000 --- a/sdk/go/tests/compatibility/sandbox_updates_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package compatibility_test - -import ( - "context" - "net" - "testing" - - dm "github.com/NVIDIA/OpenShell/sdk/go/proto/datamodelv1" - pb "github.com/NVIDIA/OpenShell/sdk/go/proto/openshellv1" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" -) - -func TestSandboxStatusRetainsBaselinePublicShape(t *testing.T) { - // Freeze the pre-image-update fields and tags: private operation state must - // not enlarge the public Rust struct or leak into generated SDK messages. - fields := []string{ - "agent_pod", "agent_fd", "sandbox_fd", "conditions", "phase", - "current_policy_version", "main_process_instance_id", "exit_code", - "endpoint_statuses", "configuration_admission", "configuration_activated", - "provisioning", "restart_count", "next_restart_time", - "main_process_started_time", "last_token_grant_failure", - } - descriptor := (&pb.SandboxStatus{}).ProtoReflect().Descriptor() - require.Equal(t, len(fields), descriptor.Fields().Len()) - for i, name := range fields { - field := descriptor.Fields().ByName(protoreflect.Name(name)) - require.NotNil(t, field, name) - require.Equal(t, protoreflect.FieldNumber(i+2), field.Number(), name) - } - require.Nil(t, pb.File_openshell_proto.Messages().ByName("SandboxImageUpdate")) - legacy := pb.File_openshell_proto.Services().ByName("OpenShell") - require.Nil(t, legacy.Methods().ByName("UpdateSandbox")) - updates := pb.File_openshell_proto.Services().ByName("SandboxUpdates") - require.NotNil(t, updates) - require.Equal(t, 1, updates.Methods().Len()) - method := updates.Methods().Get(0) - require.Equal(t, protoreflect.FullName("openshell.v1.UpdateSandboxRequest"), method.Input().FullName()) - require.Equal(t, protoreflect.FullName("openshell.v1.SandboxResponse"), method.Output().FullName()) - require.Equal(t, "/openshell.v1.SandboxUpdates/UpdateSandbox", pb.SandboxUpdates_UpdateSandbox_FullMethodName) -} - -type legacyHealthServer struct { - pb.UnimplementedOpenShellServer -} - -func (legacyHealthServer) Health(context.Context, *pb.HealthRequest) (*pb.HealthResponse, error) { - return &pb.HealthResponse{Version: "legacy-compatible"}, nil -} - -type imageUpdateServer struct { - pb.UnimplementedSandboxUpdatesServer - request *pb.UpdateSandboxRequest -} - -func (s *imageUpdateServer) UpdateSandbox(_ context.Context, request *pb.UpdateSandboxRequest) (*pb.SandboxResponse, error) { - s.request = proto.Clone(request).(*pb.UpdateSandboxRequest) - return &pb.SandboxResponse{Sandbox: &pb.Sandbox{ - Metadata: &dm.ObjectMeta{Id: "stable-id", Name: request.Name, Workspace: request.WorkspaceScope.GetWorkspace()}, - Spec: &pb.SandboxSpec{Template: &pb.SandboxTemplate{Image: request.GetImage()}}, - }}, nil -} - -func TestSeparateSandboxUpdatesTransportCoexistsWithLegacyOpenShell(t *testing.T) { - listener := bufconn.Listen(1024 * 1024) - server := grpc.NewServer(grpc.UnaryInterceptor(func(ctx context.Context, request any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { - if info.FullMethod == pb.SandboxUpdates_UpdateSandbox_FullMethodName { - md, _ := metadata.FromIncomingContext(ctx) - if len(md.Get("authorization")) == 0 { - return nil, status.Error(codes.Unauthenticated, "bearer required") - } - } - return handler(ctx, request) - })) - updatesServer := &imageUpdateServer{} - pb.RegisterOpenShellServer(server, legacyHealthServer{}) - pb.RegisterSandboxUpdatesServer(server, updatesServer) - go func() { _ = server.Serve(listener) }() - t.Cleanup(server.Stop) - t.Cleanup(func() { _ = listener.Close() }) - conn, err := grpc.NewClient("passthrough:///sandbox-updates", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { - return listener.Dial() - })) - require.NoError(t, err) - t.Cleanup(func() { _ = conn.Close() }) - legacy := pb.NewOpenShellClient(conn) - health, err := legacy.Health(context.Background(), &pb.HealthRequest{}) - require.NoError(t, err) - require.Equal(t, "legacy-compatible", health.Version) - updates := pb.NewSandboxUpdatesClient(conn) - request := &pb.UpdateSandboxRequest{ - WorkspaceScope: &dm.WorkspaceSelector{Selection: &dm.WorkspaceSelector_Workspace{Workspace: "team"}}, - Name: "sandbox", - Image: proto.String("example/workload:new"), - ExpectedResourceVersion: 42, - RequestId: "550e8400-e29b-41d4-a716-446655440000", - } - _, err = updates.UpdateSandbox(context.Background(), request) - require.Equal(t, codes.Unauthenticated, status.Code(err)) - require.Nil(t, updatesServer.request) - ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("authorization", "Bearer test-token")) - response, err := updates.UpdateSandbox(ctx, request) - require.NoError(t, err) - require.True(t, proto.Equal(request, updatesServer.request)) - require.Equal(t, "stable-id", response.Sandbox.Metadata.Id) - require.Equal(t, "team", response.Sandbox.Metadata.Workspace) - require.Equal(t, request.GetImage(), response.Sandbox.Spec.Template.Image) - // The old service has no accidental update endpoint. - err = conn.Invoke(ctx, "/openshell.v1.OpenShell/UpdateSandbox", request, &pb.SandboxResponse{}) - require.Equal(t, codes.Unimplemented, status.Code(err)) -} diff --git a/tasks/go.toml b/tasks/go.toml deleted file mode 100644 index 4ad689c52c..0000000000 --- a/tasks/go.toml +++ /dev/null @@ -1,220 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -# Go SDK development, build, lint, and format tasks - -["go:test"] -description = "Run Go SDK unit tests with coverage" -dir = "sdk/go" -run = """ -#!/usr/bin/env bash -set -euo pipefail - -if [ "$(go env GOOS)" = "windows" ] && [ "$(go env GOARCH)" = "arm64" ]; then - echo "Go race detection is unsupported on windows/arm64; running without -race." - go test -coverprofile=coverage.out -coverpkg=./openshell/... ./... -else - go test -coverprofile=coverage.out -coverpkg=./openshell/... -race ./... -fi -""" -hide = true - -["go:test:integration"] -description = "Run Go SDK integration tests" -dir = "sdk/go" -run = "go test -tags=integration -race ./..." -hide = true - -["go:lint"] -description = "Run Go SDK linter" -dir = "sdk/go" -run = "golangci-lint run ./..." -hide = true - -["go:fmt"] -description = "Format Go SDK code" -dir = "sdk/go" -run = "goimports -w . && go fmt ./..." -hide = true - -["go:build"] -description = "Build Go SDK packages" -dir = "sdk/go" -run = "go build ./..." -hide = true - -["go:format:check"] -description = "Verify Go SDK code is gofmt-formatted" -dir = "sdk/go" -run = """ -#!/usr/bin/env bash -set -euo pipefail -UNFORMATTED=$(gofmt -l . 2>/dev/null || true) -if [ -n "$UNFORMATTED" ]; then - echo "ERROR: The following files are not gofmt-formatted:" - echo "$UNFORMATTED" - exit 1 -fi -""" -hide = true - -["go:ci"] -description = "Run Go SDK full CI pipeline" -depends = ["go:format:check", "go:lint", "go:build", "go:test", "go:proto:check", "go:docs:check"] - -["go:docs:check"] -description = "Verify every public Go SDK package has a docs page" -dir = "sdk/go" -run = """ -#!/usr/bin/env bash -set -euo pipefail - -DOCS_DIR="docs/src/api" -SUMMARY="docs/src/SUMMARY.md" -MISSING=0 - -# Find all public packages with a doc.go (excluding internal, proto, types) -for docfile in openshell/v1/*/doc.go; do - pkg=$(basename "$(dirname "$docfile")") - - # Skip internal packages and types (no user-facing docs needed) - case "$pkg" in - internal|types) continue ;; - esac - - # Check for matching docs page - if [ ! -f "$DOCS_DIR/$pkg.md" ]; then - echo "MISSING: $DOCS_DIR/$pkg.md (package openshell/v1/$pkg has doc.go but no docs page)" - MISSING=$((MISSING + 1)) - fi - - # Check for SUMMARY.md entry - if ! grep -q "api/$pkg.md" "$SUMMARY" 2>/dev/null; then - echo "MISSING: SUMMARY.md entry for api/$pkg.md" - MISSING=$((MISSING + 1)) - fi -done - -if [ "$MISSING" -gt 0 ]; then - echo "" - echo "ERROR: $MISSING documentation gaps found." - echo "Every public package with doc.go needs a docs/src/api/.md page" - echo "and a SUMMARY.md entry. See Constitution XIII." - exit 1 -fi - -echo "Docs check passed: all public packages have documentation." -""" -hide = true - -["go:proto:gen"] -description = "Generate Go bindings from proto files using buf" -dir = "sdk/go" -run = """ -#!/usr/bin/env bash -set -euo pipefail - -SDK_ROOT=$(pwd -P) -REPO_ROOT=$(cd ../.. && pwd -P) - -for tool in buf protoc-gen-go protoc-gen-go-grpc; do - if ! command -v "$tool" &>/dev/null; then - echo "ERROR: $tool not found. Run 'mise install' to install it." - exit 1 - fi -done - -if find proto -maxdepth 1 -name '*.proto' -print -quit | grep -q .; then - echo "ERROR: sdk/go/proto must contain generated bindings only." - echo "Proto sources belong in the repository root proto/ directory." - exit 1 -fi - -BUF_TEMPLATE="$SDK_ROOT/buf.gen.yaml" -if command -v cygpath &>/dev/null; then - WORK_DIR=$(mktemp -d) - trap 'rm -rf "$WORK_DIR"' EXIT - GO_PLUGIN=$(cygpath -m "$(command -v protoc-gen-go)") - GRPC_PLUGIN=$(cygpath -m "$(command -v protoc-gen-go-grpc)") - GO_PLUGIN=${GO_PLUGIN//&/\\&} - GRPC_PLUGIN=${GRPC_PLUGIN//&/\\&} - sed \ - -e "s|local: protoc-gen-go$|local: $GO_PLUGIN|" \ - -e "s|local: protoc-gen-go-grpc$|local: $GRPC_PLUGIN|" \ - "$SDK_ROOT/buf.gen.yaml" > "$WORK_DIR/buf.gen.yaml" - BUF_TEMPLATE="$WORK_DIR/buf.gen.yaml" -fi - -# Clean previous output before regeneration -find proto -name '*.pb.go' -delete 2>/dev/null || true -find proto -mindepth 1 -type d -empty -delete 2>/dev/null || true - -(cd "$REPO_ROOT" && buf generate --template "$BUF_TEMPLATE") - -echo "Proto generation complete." -echo "Generated packages:" -for pkg_dir in proto/*/; do - count=$(find "$pkg_dir" -maxdepth 1 -name '*.go' | wc -l | tr -d ' ') - echo " $pkg_dir: $count files" -done -""" -hide = true - -["go:proto:check"] -description = "Verify generated Go SDK proto files are up to date" -dir = "sdk/go" -run = """ -#!/usr/bin/env bash -set -euo pipefail - -SDK_ROOT=$(pwd -P) -REPO_ROOT=$(cd ../.. && pwd -P) - -for tool in buf protoc-gen-go protoc-gen-go-grpc; do - if ! command -v "$tool" &>/dev/null; then - echo "ERROR: $tool not found. Run 'mise install' to install it." - exit 1 - fi -done - -WORK_DIR=$(mktemp -d) -trap 'rm -rf "$WORK_DIR"' EXIT - -if find proto -maxdepth 1 -name '*.proto' -print -quit | grep -q .; then - echo "ERROR: sdk/go/proto contains copied proto sources." - echo "Proto sources belong in the repository root proto/ directory." - exit 1 -fi - -# Generate to temp directory with adjusted output path -BUF_OUTPUT_DIR="$WORK_DIR" -if command -v cygpath &>/dev/null; then - BUF_OUTPUT_DIR=$(cygpath -m "$WORK_DIR") -fi -sed "s|out: sdk/go|out: $BUF_OUTPUT_DIR|" "$SDK_ROOT/buf.gen.yaml" > "$WORK_DIR/buf.gen.yaml" -if command -v cygpath &>/dev/null; then - GO_PLUGIN=$(cygpath -m "$(command -v protoc-gen-go)") - GRPC_PLUGIN=$(cygpath -m "$(command -v protoc-gen-go-grpc)") - GO_PLUGIN=${GO_PLUGIN//&/\\&} - GRPC_PLUGIN=${GRPC_PLUGIN//&/\\&} - sed \ - -e "s|local: protoc-gen-go$|local: $GO_PLUGIN|" \ - -e "s|local: protoc-gen-go-grpc$|local: $GRPC_PLUGIN|" \ - "$WORK_DIR/buf.gen.yaml" > "$WORK_DIR/buf.gen.windows.yaml" - mv "$WORK_DIR/buf.gen.windows.yaml" "$WORK_DIR/buf.gen.yaml" -fi -(cd "$REPO_ROOT" && buf generate --template "$WORK_DIR/buf.gen.yaml") - -DIFF_OUTPUT=$(diff -r "$WORK_DIR/proto" "$SDK_ROOT/proto" 2>&1) || true - -if [ -n "$DIFF_OUTPUT" ]; then - echo "ERROR: Generated proto files are out of date." - echo "Run 'mise run go:proto:gen' to regenerate." - echo "" - echo "$DIFF_OUTPUT" - exit 1 -fi - -echo "Proto check passed: generated files are up to date." -""" -hide = true From 751fc4fdbe20147b4375111ca4e968ae388349b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Hu=C3=9F?= Date: Wed, 4 Nov 2026 16:13:57 +0100 Subject: [PATCH 2/2] ci: drop the go-sdk compatibility surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With sdk/go removed, the compatibility review has no Go SDK left to review. Remove go-sdk from the required surfaces in the review prompt and in the report validator, which rejects reviews that don't cover exactly the listed surfaces, and drop Go from the CI.md descriptions of the compatibility review, CodeQL, and the release publishers. Signed-off-by: Roland Huß --- .github/prompts/compatibility-review.md | 15 +++------------ CI.md | 10 +++++----- tasks/scripts/codex_compatibility_report.py | 1 - 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/.github/prompts/compatibility-review.md b/.github/prompts/compatibility-review.md index 421b23e482..24d674dc4e 100644 --- a/.github/prompts/compatibility-review.md +++ b/.github/prompts/compatibility-review.md @@ -113,8 +113,9 @@ needed. ## Required surfaces -Account for every surface: `rust-sdk`, `python-sdk`, `go-sdk`, `typescript-sdk`, -`api-behavior`, `cli`, `configuration`, `policy`, `helm`, and `state`. +Account for every surface: `rust-sdk`, `python-sdk`, `typescript-sdk`, +`api-behavior`, `cli`, `configuration`, `policy`, `helm`, and `state`. The Go +SDK moved to ai-openshell/sdk-go and is not a surface of this repository. Scope `rust-sdk` to the public API reachable through `openshell-sdk`, including its `raw` module and transitive public re-exports. Trace the export path before @@ -126,16 +127,6 @@ generated server-trait changes reachable through `raw::proto`, identifying mock/server implementers as affected consumers and distinguishing them from unaffected client callers. -For `go-sdk`, adopt the [Go struct-literal compatibility convention](https://go.dev/doc/go1compat): -adding fields to exported structs is additive when existing keyed literals remain -valid and omitted fields preserve existing behavior. Do not report a break solely -because unkeyed (positional) literals need updating, unless the baseline explicitly -guarantees that construction form. Still inspect existing field names and types, -zero-value behavior, comparability (equality and map keys), and serialization -contracts; a field addition can break those independently. Follow the -[Go module compatibility guidance](https://go.dev/blog/module-compatibility#maintaining-struct-compatibility) -for these limits. This exception applies only to Go struct-field additions. - Check exported signatures/types, generated and handwritten SDK behavior, serialization and error contracts, command flags and documented machine-readable output, config/default semantics, policy formats, chart values, and persisted diff --git a/CI.md b/CI.md index 90fe3fae0e..41d4b60742 100644 --- a/CI.md +++ b/CI.md @@ -94,8 +94,8 @@ qualification. Branch checks compare the prospective merge tree with its target, using workflow-revision review tooling and the target's tag-derived train policy. Release qualification reviews the cumulative diff from the previous reachable stable release to the exact candidate commit, using the same tag-derived -patch/minor policy as the protobuf check. It reviews public Rust, Python, Go, -and TypeScript SDKs, API behavior, CLI contracts, configuration, policy, Helm, +patch/minor policy as the protobuf check. It reviews public Rust, Python, and +TypeScript SDKs, API behavior, CLI contracts, configuration, policy, Helm, and persisted state. Both Codex workflows share the `codex-release-review` concurrency group with `queue: max`, so only one review runs at a time and up to 100 pending reviews @@ -245,7 +245,7 @@ scanners upload results directly: warn-only mode. A preflight turns an unavailable GitHub Dependency Graph into a warning, so the workflow remains neutral until the repository feature is available. -- `CodeQL` analyzes product Rust code, examples, and the Go, Python, and +- `CodeQL` analyzes product Rust code, examples, and the Python and TypeScript SDKs. Rust `cfg(test)` blocks, Rust integration-test targets, and E2E test code are excluded. It runs nightly on `main`, remains manually dispatchable for diagnostics, uploads results to Code Scanning, and retains @@ -707,7 +707,7 @@ The bot's full administrator documentation is internal to NVIDIA. The only comma | `.github/workflows/e2e-label-help.yml` | When a `test:e2e*` label is applied, posts a PR comment telling the maintainer the next manual step (re-run an existing workflow run, or `/ok to test ` to refresh the mirror). | | `.github/workflows/workflow-security.yml` | Runs informational Actionlint and High-severity Zizmor reports on GitHub-hosted runners. | | `.github/workflows/dependency-review.yml` | Reports dependency changes when GitHub Dependency Graph is available; otherwise publishes a neutral warning. | -| `.github/workflows/codeql.yml` | Runs nightly informational CodeQL analysis on `main` for Rust and the Go, Python, and TypeScript SDKs and retains SARIF artifacts. | +| `.github/workflows/codeql.yml` | Runs nightly informational CodeQL analysis on `main` for Rust and the Python and TypeScript SDKs and retains SARIF artifacts. | | `.github/workflows/codex-security.yml` | Advisory scan during prerelease qualification; scans the stable-to-candidate diff and publishes train-scoped SARIF on `main`. | | `.github/workflows/codex-compatibility.yml` | Reviews branch and cumulative tagged-release compatibility with Codex and retains advisory findings, PR acknowledgements, and coverage; it does not gate merging or publication. | | `.github/workflows/trivy-changes.yml` | Blocks pull requests and merge groups that introduce new High or Critical Helm or Dockerfile misconfigurations. | @@ -726,7 +726,7 @@ Each step that publishes to an external registry, store, or site lives in its ow | `.github/workflows/release-auto-tag.yml` | Weekdays at 14:00 Europe/Zurich, tags the next `-pre.N` on `main` when commits landed since the latest one, then dispatches Pre-release. It skips when the train has no `-pre.1` or its version is already stable, so push each train's `-pre.1` tag by hand. | | `.github/workflows/release-prerelease.yml` | Pre-release orchestrator, triggered by prerelease tag pushes or dispatched by Release Auto-Tag or manually. A tagless dispatch creates the next candidate on its branch; explicit tags select existing candidates. Builds, then publishes and qualifies in parallel. Qualification failure fails the run and sends the failure notification; published artifacts remain available. | | `.github/workflows/release-build.yml` | Shared version computation, binary and image builds, and package builds. Validates that the tag matches the caller's release kind. | -| `.github/workflows/release-publish.yml` | Shared artifact assembly, OCI image tags and Helm chart publication, and package publication. Stable releases also publish release-labeled public schemas to Buf (`BUF_TOKEN` required) and GitHub Releases, and call the snap, npm, and Go SDK publishers. The Python SDK is released from [ai-openshell/sdk-python](https://github.com/ai-openshell/sdk-python), so no GitHub Release carries a wheel. | +| `.github/workflows/release-publish.yml` | Shared artifact assembly, OCI image tags and Helm chart publication, and package publication. Stable releases also publish release-labeled public schemas to Buf (`BUF_TOKEN` required) and GitHub Releases, and call the snap and npm publishers. The Python SDK is released from [ai-openshell/sdk-python](https://github.com/ai-openshell/sdk-python), so no GitHub Release carries a wheel. | | `.github/workflows/release-notes.yml` | Shared prompt-driven release notes generation through Inference Hub, used by prerelease previews, notes-only previews, and stable releases. Stable releases reuse the promoted prerelease's preview when available. | | `.github/workflows/release-qualification.yml` | Reusable qualification suites and result aggregation. Runs security scans, conformance and feature integration, Docker and VM E2E, protobuf compatibility, and advisory compatibility review. | | `.github/workflows/snap-package.yml` | Builds snap and component artifacts for Release Dev, Release, and Pre-release without Store credentials or publication. | diff --git a/tasks/scripts/codex_compatibility_report.py b/tasks/scripts/codex_compatibility_report.py index 1c959ef909..2c1b3c91f1 100644 --- a/tasks/scripts/codex_compatibility_report.py +++ b/tasks/scripts/codex_compatibility_report.py @@ -11,7 +11,6 @@ SURFACES = ( "rust-sdk", "python-sdk", - "go-sdk", "typescript-sdk", "api-behavior", "cli",